Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-16 15:14:12 +00:00
parent 37a0f5e2cf
commit 282e71d660
39 changed files with 1262 additions and 432 deletions

View File

@ -13,6 +13,7 @@ class Environment < ApplicationRecord
self.reactive_cache_work_type = :external_dependency
belongs_to :project, optional: false
belongs_to :merge_request, optional: true
use_fast_destroy :all_deployments
nullify_if_blank :external_url
@ -68,6 +69,7 @@ class Environment < ApplicationRecord
allow_nil: true
validate :safe_external_url
validate :merge_request_not_changed
delegate :manual_actions, :other_manual_actions, to: :last_deployment, allow_nil: true
delegate :auto_rollback_enabled?, to: :project
@ -524,6 +526,12 @@ class Environment < ApplicationRecord
self.tier ||= guess_tier
end
def merge_request_not_changed
if merge_request_id_changed? && persisted?
errors.add(:merge_request, 'merge_request cannot be changed')
end
end
# Guessing the tier of the environment if it's not explicitly specified by users.
# See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
def guess_tier

View File

@ -116,6 +116,7 @@ class MergeRequest < ApplicationRecord
has_many :draft_notes
has_many :reviews, inverse_of: :merge_request
has_many :created_environments, class_name: 'Environment', foreign_key: :merge_request_id, inverse_of: :merge_request
KNOWN_MERGE_PARAMS = [
:auto_merge_strategy,

View File

@ -25,8 +25,19 @@ module Environments
def execute_for_merge_request_pipeline(merge_request)
return unless merge_request.actual_head_pipeline&.merge_request?
merge_request.environments_in_head_pipeline(deployment_status: :success).each do |environment|
execute(environment)
created_environments = merge_request.created_environments
if created_environments.any?
created_environments.each { |env| execute(env) }
else
environments_in_head_pipeline = merge_request.environments_in_head_pipeline(deployment_status: :success)
environments_in_head_pipeline.each { |env| execute(env) }
if environments_in_head_pipeline.any?
# If we don't see a message often, we'd be able to remove this path. (or likely in GitLab 16.0)
# See https://gitlab.com/gitlab-org/gitlab/-/issues/372965
Gitlab::AppJsonLogger.info(message: 'Running legacy dynamic environment stop logic', project_id: project.id)
end
end
end

View File

@ -16,7 +16,7 @@
%span.gl-text-truncate.gl-sm-ml-3
= key.fingerprint
.gl-mt-3= s_('Profiles|Created%{time_ago}'.html_safe) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2')}
.gl-mt-3= html_escape(s_('Profiles|Created%{time_ago}')) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2').html_safe}
.key-list-item-dates
%span.last-used-at.gl-mr-3

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddMergeRequestIdToEnvironments < Gitlab::Database::Migration[2.0]
def change
add_column :environments, :merge_request_id, :bigint
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class IndexEvironmentsOnMergeRequestId < Gitlab::Database::Migration[2.0]
INDEX_NAME = 'index_environments_on_merge_request_id'
disable_ddl_transaction!
def up
add_concurrent_index :environments, :merge_request_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :environments, INDEX_NAME
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddFkConstraintToEnvironmentsMergeRequestId < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :environments, :merge_requests, column: :merge_request_id, on_delete: :nullify
end
def down
remove_foreign_key_if_exists :environments, column: :merge_request_id
end
end

View File

@ -0,0 +1 @@
3e29afa3670370b8f5801523711d0689f1228a880b1941c44798f4bc76bedbb0

View File

@ -0,0 +1 @@
b29e850775a327dcf6e37e25a43066a0638a55a4e0bd6b818cf496f0b97c6f82

View File

@ -0,0 +1 @@
5b1c25848e3e890fe27c3a43effce093af5f0fe42118c7976919acef84387a0a

View File

@ -15036,7 +15036,8 @@ CREATE TABLE environments (
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone,
tier smallint
tier smallint,
merge_request_id bigint
);
CREATE SEQUENCE environments_id_seq
@ -28625,6 +28626,8 @@ CREATE INDEX index_emails_on_user_id ON emails USING btree (user_id);
CREATE INDEX index_enabled_clusters_on_id ON clusters USING btree (id) WHERE (enabled = true);
CREATE INDEX index_environments_on_merge_request_id ON environments USING btree (merge_request_id);
CREATE INDEX index_environments_on_name_varchar_pattern_ops ON environments USING btree (name varchar_pattern_ops);
CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments USING btree (project_id, name);
@ -32273,6 +32276,9 @@ ALTER TABLE ONLY deployments
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_013c9f36ca FOREIGN KEY (due_date_sourcing_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
ALTER TABLE ONLY environments
ADD CONSTRAINT fk_01a033a308 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE SET NULL;
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_0314ee86eb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

View File

@ -124,6 +124,7 @@ The following are required to run Geo:
- Git 2.9 or later
- Git-lfs 2.4.2 or later on the user side when using LFS
- All sites must run [the same GitLab and PostgreSQL versions](setup/database.md#postgresql-replication).
- If using different operating system versions between Geo sites, [check OS locale data compatibility](replication/troubleshooting.md#check-os-locale-data-compatibility) across Geo sites.
Additionally, check the GitLab [minimum requirements](../../install/requirements.md),
and we recommend you use the latest version of GitLab for a better experience.

View File

@ -1234,3 +1234,33 @@ If the above steps are **not successful**, proceed through the next steps:
## Additional tools
There are useful snippets for manipulating Geo internals in the [GitLab Rails Cheat Sheet](../../troubleshooting/gitlab_rails_cheat_sheet.md#geo). For example, you can find how to manually sync or verify a replicable in Rails console.
## Check OS locale data compatibility
If different operating systems or different operating system versions are deployed across Geo sites, we recommend that you perform a locale data compatibility check setting up Geo.
Geo uses PostgreSQL and Streaming Replication to replicate data across Geo sites. PostgreSQL uses locale data provided by the operating systems C library for sorting text. If the locale data in the C library is incompatible across Geo sites, erroneous query results that lead to [incorrect behavior on secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/360723). See [here](https://wiki.postgresql.org/wiki/Locale_data_changes) for more details.
On all hosts running PostgreSQL, across all Geo sites, run the following shell command:
```shell
( echo "1-1"; echo "11" ) | LC_COLLATE=en_US.UTF-8 sort
```
The output will either look like:
```plaintext
1-1
11
```
or the reverse order:
```plaintext
11
1-1
```
If the output is identical on all hosts, then they running compatible versions of locale data.
If the output differs on some hosts, then PostgreSQL replication will not work properly. We advise that you select operating system versions that are compatible.

View File

@ -648,7 +648,7 @@ installation, run the following in the [GitLab Rails console](operations/rails_c
Plan.default.actual_limits.update!(ci_max_artifact_size_junit: 10)
```
### Number of files per GitLab Pages web-site
### Number of files per GitLab Pages website
The total number of file entries (including directories and symlinks) is limited to `200,000` per
GitLab Pages website.
@ -663,6 +663,14 @@ For example, to change the limit to `100`:
Plan.default.actual_limits.update!(pages_file_entries: 100)
```
### Number of custom domains per GitLab Pages website
The total number of custom domains per GitLab Pages website is limited to `150` for [GitLab SaaS](../subscriptions/gitlab_com/index.md).
The default limit for [GitLab self-managed](../subscriptions/self_managed/index.md) is `0` (unlimited).
To set a limit on your self-managed instance, use the
[Admin Area](pages/index.md#set-maximum-number-of-gitlab-pages-custom-domains-for-a-project).
### Number of registered runners per scope
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12. Disabled by default.

View File

@ -1113,6 +1113,19 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
--form "avatar=@/tmp/example.png"
```
### Remove a group avatar
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96421) in GitLab 15.4.
To remove a group avatar, use a blank value for the `avatar` attribute.
Example request:
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22" \
--data "avatar="
```
## Remove group
> - Immediately deleting subgroups was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360008) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `immediate_delete_subgroup_api`. Disabled by default.

View File

@ -8,6 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## List project repository tags
> `version` value for the `order_by` attribute [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95150) in GitLab 15.4.
Get a list of repository tags from a project, sorted by update date and time in descending order. This endpoint can be accessed without authentication if the
repository is publicly accessible.
@ -19,9 +21,9 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string| yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user|
| `order_by` | string | no | Return tags ordered by `name`, `updated`, or `version` (since 15.4) fields. Default is `updated` |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
| `id` | integer or string| yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `order_by` | string | no | Return tags ordered by `name`, `updated`, or `version`. Default is `updated`. |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc`. |
| `search` | string | no | Return list of tags matching the search criteria. You can use `^term` and `term$` to find tags that begin and end with `term` respectively. No other regular expressions are supported. |
```json
@ -115,7 +117,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |
| `ref` | string | yes | Create tag using commit SHA, another tag name, or branch name |
| `message` | string | no | Creates annotated tag |
@ -172,5 +174,5 @@ Parameters:
| Attribute | Type | Required | Description |
| ---------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of a tag |

View File

@ -168,7 +168,7 @@ Required options:
```yaml
time_frame: all
data_source: redis
instrumentation_class: 'RedisMetric'
instrumentation_class: RedisMetric
options:
event: pushes
prefix: source_code
@ -199,7 +199,7 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: all
data_source: redis
instrumentation_class: 'MergeUsageCountRedisMetric'
instrumentation_class: MergeUsageCountRedisMetric
options:
event: pushes
prefix: source_code
@ -217,7 +217,7 @@ Count unique values for `i_quickactions_approve` event.
```yaml
time_frame: 28d
data_source: redis_hll
instrumentation_class: 'RedisHLLMetric'
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_approve
@ -248,7 +248,7 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: 28d
data_source: redis_hll
instrumentation_class: 'MergeUsageCountRedisHLLMetric'
instrumentation_class: MergeUsageCountRedisHLLMetric
options:
events:
- i_quickactions_approve
@ -288,7 +288,7 @@ You must also include the instrumentation class name in the YAML setup.
```yaml
time_frame: 28d
instrumentation_class: 'IssuesBoardsCountMetric'
instrumentation_class: IssuesBoardsCountMetric
```
## Generic metrics
@ -345,7 +345,7 @@ The generator takes the class name as an argument and the following options:
- `--ee` Indicates if the metric is for EE.
```shell
rails generate gitlab:usage_metric CountIssues --type database
rails generate gitlab:usage_metric CountIssues --type database --operation distinct_count
create lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
create spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb
```

View File

@ -16,7 +16,6 @@ GitLab has two types of namespaces:
read about [repository redirects](../project/repository/index.md#what-happens-when-a-repository-path-changes).
- You cannot create subgroups in a personal namespace.
- Groups in your namespace do not inherit your namespace permissions and group features.
- Creating subgroups under this namespace is not allowed.
- All the *Personal Projects* created will fall under the scope of this namespace.
- A *group* or *subgroup* namespace:

View File

@ -102,6 +102,10 @@ To delete a task:
To show who is responsible for a task, you can assign users to it.
Users on GitLab Free can assign one user per task.
Users on GitLab Premium and higher can assign multiple users to a single task.
See also [multiple assignees for issues](project/issues/multiple_assignees_for_issues.md).
Prerequisites:
- You must have at least the Reporter role for the project.

View File

@ -248,6 +248,8 @@ module API
authorize! :admin_group, group
group.remove_avatar! if params.key?(:avatar) && params[:avatar].nil?
if update_group(group)
present_group_details(params, group, with_projects: true)
else

View File

@ -100,6 +100,62 @@ module API
present release, with: Entities::Release, current_user: current_user, include_html_description: params[:include_html_description]
end
desc 'Download a project release asset file' do
detail 'This feature was introduced in GitLab 15.4.'
named 'download_release_asset_file'
end
params do
requires :tag_name, type: String,
desc: 'The name of the tag.', as: :tag
requires :file_path, type: String,
file_path: true,
desc: 'The path to the file to download, as specified when creating the release asset.'
end
route_setting :authentication, job_token_allowed: true
get ':id/releases/:tag_name/downloads/*file_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
not_found! unless release
link = release.links.find_by_filepath!("/#{params[:file_path]}")
not_found! unless link
redirect link.url
end
desc 'Get the latest project release' do
detail 'This feature was introduced in GitLab 15.4.'
named 'get_latest_release'
end
params do
requires :suffix_path, type: String, file_path: true, desc: 'The path to be suffixed to the latest release'
end
route_setting :authentication, job_token_allowed: true
get ':id/releases/permalink/latest(/)(*suffix_path)', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
# Try to find the latest release
latest_release = find_latest_release
not_found! unless latest_release
# Build the full API URL with the tag of the latest release
redirect_url = api_v4_projects_releases_path(id: user_project.id, tag_name: latest_release.tag)
# Include the additional suffix_path if present
redirect_url += "/#{params[:suffix_path]}" if params[:suffix_path].present?
# Include any query parameter except `order_by` since we have plans to extend it in the future.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/352945 for reference.
query_parameters_except_order_by = get_query_params.except('order_by')
if query_parameters_except_order_by.present?
redirect_url += "?#{query_parameters_except_order_by.compact.to_param}"
end
redirect redirect_url
end
desc 'Create a new release' do
detail 'This feature was introduced in GitLab 11.7.'
named 'create_release'
@ -232,6 +288,16 @@ module API
@release ||= user_project.releases.find_by_tag(params[:tag])
end
def find_latest_release
ReleasesFinder.new(user_project, current_user, { order_by: 'released_at', sort: 'desc' }).execute.first
end
def get_query_params
return {} unless @request.query_string.present?
Rack::Utils.parse_nested_query(@request.query_string)
end
def log_release_created_audit_event(release)
# extended in EE
end

View File

@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::<%= class_name %>Metric do
it_behaves_like 'a correct instrumented metric value', {}, 1
let(:expected_value) { 1 }
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end

View File

@ -18,7 +18,9 @@ module Gitlab
def ensure_environment(build)
return unless build.instance_of?(::Ci::Build) && build.has_environment?
environment = ::Gitlab::Ci::Pipeline::Seed::Environment.new(build).to_resource
environment = ::Gitlab::Ci::Pipeline::Seed::Environment
.new(build, merge_request: @command.merge_request)
.to_resource
if environment.persisted?
build.persisted_environment = environment

View File

@ -5,12 +5,13 @@ module Gitlab
module Pipeline
module Seed
class Environment < Seed::Base
attr_reader :job
attr_reader :job, :merge_request
delegate :simple_variables, to: :job
def initialize(job)
def initialize(job, merge_request: nil)
@job = job
@merge_request = merge_request
end
def to_resource
@ -18,6 +19,7 @@ module Gitlab
# Initialize the attributes at creation
environment.auto_stop_in = expanded_auto_stop_in
environment.tier = deployment_tier
environment.merge_request = merge_request
end
end

View File

@ -30403,6 +30403,9 @@ msgstr ""
msgid "Profiles|Connected Accounts"
msgstr ""
msgid "Profiles|Created%{time_ago}"
msgstr ""
msgid "Profiles|Current path: %{path}"
msgstr ""

View File

@ -1,13 +1,63 @@
# frozen_string_literal: true
require "spec_helper"
require 'spec_helper'
RSpec.describe "User creates branch", :js do
RSpec.describe 'User creates branch', :js do
include Spec::Support::Helpers::Features::BranchesHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:user) { create(:user) }
shared_examples 'creates new branch' do
specify do
branch_name = "deploy_keys_#{SecureRandom.hex(4)}"
create_branch(branch_name)
expect(page).to have_content(branch_name)
end
end
shared_examples 'renders not found page' do
specify do
expect(page).to have_title('Not Found')
expect(page).to have_content('Page Not Found')
end
end
context 'when project is public with private repository' do
let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'renders not found page'
end
context 'and user is a developer' do
before do
group.add_developer(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'creates new branch'
end
end
end
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :repository, group: group) }
context 'when user is a direct project member' do
context 'and user is a developer' do
before do
project.add_developer(user)
sign_in(user)
@ -15,7 +65,7 @@ RSpec.describe "User creates branch", :js do
visit(new_project_branch_path(project))
end
context 'on new branch page' do
context 'when on new branch page' do
it 'renders I18n supported text' do
page.within('#new-branch-form') do
expect(page).to have_content(_('Branch name'))
@ -25,34 +75,55 @@ RSpec.describe "User creates branch", :js do
end
end
it "creates new branch" do
branch_name = "deploy_keys"
it_behaves_like 'creates new branch'
create_branch(branch_name)
context 'when branch name is invalid' do
it 'does not create new branch' do
invalid_branch_name = '1.0 stable'
expect(page).to have_content(branch_name)
end
fill_in('branch_name', with: invalid_branch_name)
page.find('body').click # defocus the branch_name input
context "when branch name is invalid" do
it "does not create new branch" do
invalid_branch_name = "1.0 stable"
select_branch('master')
click_button('Create branch')
fill_in("branch_name", with: invalid_branch_name)
page.find("body").click # defocus the branch_name input
select_branch("master")
click_button("Create branch")
expect(page).to have_content("Branch name is invalid")
expect(page).to have_content('Branch name is invalid')
expect(page).to have_content("can't contain spaces")
end
end
context "when branch name already exists" do
it "does not create new branch" do
create_branch("master")
context 'when branch name already exists' do
it 'does not create new branch' do
create_branch('master')
expect(page).to have_content("Branch already exists")
expect(page).to have_content('Branch already exists')
end
end
end
end
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'renders not found page'
end
context 'and user is a developer' do
before do
group.add_developer(user)
sign_in(user)
visit(new_project_branch_path(project))
end
it_behaves_like 'creates new branch'
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Runners' do
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
before do
sign_in(user)
@ -24,25 +24,25 @@ RSpec.describe 'Runners' do
end
context 'when a project has enabled shared_runners' do
let(:project) { create(:project) }
let_it_be(:project) { create(:project) }
before do
project.add_maintainer(user)
end
context 'when a project_type runner is activated on the project' do
let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
it 'user sees the specific runner' do
visit project_runners_path(project)
within '.activated-specific-runners' do
expect(page).to have_content(specific_runner.display_name)
expect(page).to have_content(project_runner.display_name)
end
click_on specific_runner.short_sha
click_on project_runner.short_sha
expect(page).to have_content(specific_runner.platform)
expect(page).to have_content(project_runner.platform)
end
it 'user can pause and resume the specific runner' do
@ -72,7 +72,7 @@ RSpec.describe 'Runners' do
click_on 'Remove runner'
end
expect(page).not_to have_content(specific_runner.display_name)
expect(page).not_to have_content(project_runner.display_name)
end
it 'user edits the runner to be protected' do
@ -92,7 +92,7 @@ RSpec.describe 'Runners' do
context 'when a runner has a tag' do
before do
specific_runner.update!(tag_list: ['tag'])
project_runner.update!(tag_list: ['tag'])
end
it 'user edits runner not to run untagged jobs' do
@ -120,11 +120,9 @@ RSpec.describe 'Runners' do
expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
end
end
end
context 'when multiple runners are configured' do
let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
let!(:specific_runner_2) { create(:ci_runner, :project, projects: [project]) }
let!(:project_runner_2) { create(:ci_runner, :project, projects: [project]) }
it 'adds pagination to the runner list' do
stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
@ -134,10 +132,11 @@ RSpec.describe 'Runners' do
expect(find('.pagination')).not_to be_nil
end
end
end
context 'when a specific runner exists in another project' do
let(:another_project) { create(:project) }
let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
let!(:project_runner) { create(:ci_runner, :project, projects: [another_project]) }
before do
another_project.add_maintainer(user)
@ -150,13 +149,13 @@ RSpec.describe 'Runners' do
click_on 'Enable for this project'
end
expect(page.find('.activated-specific-runners')).to have_content(specific_runner.display_name)
expect(page.find('.activated-specific-runners')).to have_content(project_runner.display_name)
within '.activated-specific-runners' do
click_on 'Disable for this project'
end
expect(page.find('.available-specific-runners')).to have_content(specific_runner.display_name)
expect(page.find('.available-specific-runners')).to have_content(project_runner.display_name)
end
end
@ -255,7 +254,8 @@ RSpec.describe 'Runners' do
project.add_maintainer(user)
end
let(:group) { create :group }
let_it_be(:group) { create :group }
let_it_be(:project) { create :project, group: group }
context 'as project and group maintainer' do
before do
@ -263,8 +263,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
let(:project) { create :project, group: group }
it 'group runners are not available' do
visit project_runners_path(project)
@ -280,8 +278,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
let(:project) { create :project, group: group }
it 'group runners are available' do
visit project_runners_path(project)
@ -304,10 +300,11 @@ RSpec.describe 'Runners' do
end
end
context 'project with a group but no group runner' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
context 'with group project' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
context 'project with a group but no group runner' do
it 'group runners are not available' do
visit project_runners_path(project)
@ -319,9 +316,9 @@ RSpec.describe 'Runners' do
end
context 'project with a group and a group runner' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
let_it_be(:ci_runner) do
create(:ci_runner, :group, groups: [group], description: 'group-runner')
end
it 'group runners are available' do
visit project_runners_path(project)
@ -346,4 +343,5 @@ RSpec.describe 'Runners' do
end
end
end
end
end

View File

@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountFooMetric do
it_behaves_like 'a correct instrumented metric value', {}, 1
let(:expected_value) { 1 }
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end

View File

@ -2,11 +2,13 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failures do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:environment) { project.environments.find_by_name('review/master') }
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
@ -24,14 +26,28 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
context 'when a pipeline contains a deployment job' do
let!(:job) { build(:ci_build, :start_review_app, project: project) }
it 'ensures environment existence for the job' do
context 'and the environment does not exist' do
it 'creates the environment specified by the job' do
expect { subject }.to change { Environment.count }.by(1)
expect(project.environments.find_by_name('review/master')).to be_present
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
context 'and the pipeline is for a merge request' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
end
it 'associates the environment with the merge request' do
expect { subject }.to change { Environment.count }.by(1)
expect(environment.merge_request).to eq(merge_request)
end
end
end
context 'when an environment has already been existed' do
before do
create(:environment, project: project, name: 'review/master')
@ -40,10 +56,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.not_to change { Environment.count }
expect(project.environments.find_by_name('review/master')).to be_present
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
context 'and the pipeline is for a merge request' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
end
it 'does not associate the environment with the merge request' do
expect { subject }.not_to change { Environment.count }
expect(environment.merge_request).to be_nil
end
end
end
context 'when an environment name contains an invalid character' do
@ -65,7 +93,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.to change { Environment.count }.by(1)
expect(project.environments.find_by_name('review/master')).to be_present
expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end

View File

@ -191,5 +191,34 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
context 'when merge_request is provided' do
let(:environment_name) { 'development' }
let(:attributes) { { environment: environment_name, options: { environment: { name: environment_name } } } }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:seed) { described_class.new(job, merge_request: merge_request) }
context 'and environment does not exist' do
let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
it 'creates an environment associated with the merge request' do
expect { subject }.to change { Environment.count }.by(1)
expect(subject.merge_request).to eq(merge_request)
end
end
context 'and environment already exists' do
before do
create(:environment, project: project, name: environment_name)
end
it 'does not change the merge request associated with the environment' do
expect { subject }.not_to change { Environment.count }
expect(subject.merge_request).to be_nil
end
end
end
end
end

View File

@ -211,6 +211,7 @@ merge_requests:
- user_note_authors
- cleanup_schedule
- compliance_violations
- created_environments
external_pull_requests:
- project
merge_request_diff:

View File

@ -17,6 +17,8 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to nullify_if_blank(:external_url) }
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:merge_request).optional }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:metrics_dashboard_annotations) }
it { is_expected.to have_many(:alert_management_alerts) }
@ -40,6 +42,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
expect(environment).to be_valid
end
context 'does not allow changes to merge_request' do
let(:merge_request) { create(:merge_request, source_project: project) }
it 'for an environment that has no merge request associated' do
environment = create(:environment)
environment.merge_request = merge_request
expect(environment).not_to be_valid
end
it 'for an environment that has a merge request associated' do
environment = create(:environment, merge_request: merge_request)
environment.merge_request = nil
expect(environment).not_to be_valid
end
end
end
describe 'validate and sanitize external url' do

View File

@ -31,6 +31,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.to have_many(:draft_notes) }
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) }
it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) }
context 'for forks' do
let!(:project) { create(:project) }

View File

@ -5,13 +5,21 @@ require 'spec_helper'
RSpec.describe API::Files do
include RepoHelpers
let(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be_with_refind(:user) { create(:user) }
let_it_be(:inherited_guest) { create(:user) }
let_it_be(:inherited_reporter) { create(:user) }
let_it_be(:inherited_developer) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace ) }
let(:guest) { create(:user) { |u| project.add_guest(u) } }
let(:file_path) { "files%2Fruby%2Fpopen%2Erb" }
let(:executable_file_path) { "files%2Fexecutables%2Fls" }
let(:rouge_file_path) { "%2e%2e%2f" }
let(:absolute_path) { "%2Fetc%2Fpasswd.rb" }
let(:file_path) { 'files%2Fruby%2Fpopen%2Erb' }
let(:file_name) { 'popen.rb' }
let(:last_commit_id) { '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' }
let(:content_sha256) { 'c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887' }
let(:executable_file_path) { 'files%2Fexecutables%2Fls' }
let(:invalid_file_path) { '%2e%2e%2f' }
let(:absolute_path) { '%2Fetc%2Fpasswd.rb' }
let(:invalid_file_message) { 'file_path should be a valid file path' }
let(:params) do
{
@ -46,6 +54,12 @@ RSpec.describe API::Files do
fake_class.new
end
before_all do
group.add_guest(inherited_guest)
group.add_reporter(inherited_reporter)
group.add_developer(inherited_developer)
end
before do
project.add_developer(user)
end
@ -70,10 +84,12 @@ RSpec.describe API::Files do
expect(helper.headers).to eq({ 'X-Gitlab-Test' => '1' })
end
it 'raises exception if value is an Enumerable' do
context 'when value is an Enumerable' do
it 'raises an exception' do
expect { helper.set_http_headers(test: [1]) }.to raise_error(ArgumentError)
end
end
end
shared_examples 'when path is absolute' do
it 'returns 400 when file path is absolute' do
@ -87,12 +103,12 @@ RSpec.describe API::Files do
end
end
describe "HEAD /projects/:id/repository/files/:file_path" do
describe 'HEAD /projects/:id/repository/files/:file_path' do
shared_examples_for 'repository files' do
let(:options) { {} }
it 'returns 400 when file path is invalid' do
head api(route(rouge_file_path), current_user, **options), params: params
head api(route(invalid_file_path), current_user, **options), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
@ -106,16 +122,16 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path))
expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb')
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
expect(response.headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
expect(response.headers['X-Gitlab-File-Name']).to eq(file_name)
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq(last_commit_id)
expect(response.headers['X-Gitlab-Content-Sha256']).to eq(content_sha256)
end
it 'caches sha256 of the content', :use_clean_rails_redis_caching do
head api(route(file_path), current_user, **options), params: params
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
.to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
.to eq(content_sha256)
expect_next_instance_of(Gitlab::Git::Blob) do |instance|
expect(instance).not_to receive(:load_all_data!)
@ -126,8 +142,8 @@ RSpec.describe API::Files do
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
head api(route(file_path), current_user, **options), params: params
@ -137,15 +153,15 @@ RSpec.describe API::Files do
end
context 'when mandatory params are not given' do
it "responds with a 400 status" do
head api(route("any%2Ffile"), current_user, **options)
it 'responds with a 400 status' do
head api(route('any%2Ffile'), current_user, **options)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when file_path does not exist' do
it "responds with a 404 status" do
it 'responds with a 404 status' do
params[:ref] = 'master'
head api(route('app%2Fmodels%2Fapplication%2Erb'), current_user, **options), params: params
@ -157,7 +173,7 @@ RSpec.describe API::Files do
context 'when file_path does not exist' do
include_context 'disabled repository'
it "responds with a 403 status" do
it 'responds with a 403 status' do
head api(route(file_path), current_user, **options), params: params
expect(response).to have_gitlab_http_status(:forbidden)
@ -165,15 +181,16 @@ RSpec.describe API::Files do
end
end
context 'when unauthenticated', 'and project is public' do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository files' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it "responds with a 404 status" do
context 'and project is private' do
it 'responds with a 404 status' do
current_user = nil
head api(route(file_path), current_user), params: params
@ -181,6 +198,7 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when PATs are used' do
it_behaves_like 'repository files' do
@ -190,25 +208,41 @@ RSpec.describe API::Files do
end
end
context 'when authenticated', 'as a developer' do
context 'when authenticated' do
context 'and user is a developer' do
it_behaves_like 'repository files' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { head api(route(file_path), guest), params: params }
end
end
end
end
describe "GET /projects/:id/repository/files/:file_path" do
shared_examples_for 'repository files' do
describe 'GET /projects/:id/repository/files/:file_path' do
let(:options) { {} }
shared_examples 'returns non-executable file attributes as json' do
specify do
get api(route(file_path), api_user, **options), params: params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['file_path']).to eq(CGI.unescape(file_path))
expect(json_response['file_name']).to eq(file_name)
expect(json_response['last_commit_id']).to eq(last_commit_id)
expect(json_response['content_sha256']).to eq(content_sha256)
expect(json_response['execute_filemode']).to eq(false)
expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
end
end
shared_examples_for 'repository files' do
it 'returns 400 for invalid file path' do
get api(route(rouge_file_path), api_user, **options), params: params
get api(route(invalid_file_path), api_user, **options), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@ -218,17 +252,7 @@ RSpec.describe API::Files do
subject { get api(route(absolute_path), api_user, **options), params: params }
end
it 'returns file attributes as json' do
get api(route(file_path), api_user, **options), params: params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['file_path']).to eq(CGI.unescape(file_path))
expect(json_response['file_name']).to eq('popen.rb')
expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
expect(json_response['content_sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
expect(json_response['execute_filemode']).to eq(false)
expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
end
it_behaves_like 'returns non-executable file attributes as json'
context 'for executable file' do
it 'returns file attributes as json' do
@ -247,7 +271,7 @@ RSpec.describe API::Files do
end
it 'returns json when file has txt extension' do
file_path = "bar%2Fbranch-test.txt"
file_path = 'bar%2Fbranch-test.txt'
get api(route(file_path), api_user, **options), params: params
@ -277,8 +301,8 @@ RSpec.describe API::Files do
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
get api(route(file_path), api_user, **options), params: params
@ -289,9 +313,9 @@ RSpec.describe API::Files do
end
it 'returns raw file info' do
url = route(file_path) + "/raw"
url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, api_user, **options), params)
expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
it 'returns blame file info' do
@ -303,16 +327,16 @@ RSpec.describe API::Files do
end
it 'sets inline content disposition by default' do
url = route(file_path) + "/raw"
url = route(file_path) + '/raw'
get api(url, api_user, **options), params: params
expect(headers['Content-Disposition']).to eq(%q(inline; filename="popen.rb"; filename*=UTF-8''popen.rb))
expect(headers['Content-Disposition']).to eq(%(inline; filename="#{file_name}"; filename*=UTF-8''#{file_name}))
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user, **options) }
let(:request) { get api(route('any%2Ffile'), current_user, **options) }
end
end
@ -334,7 +358,8 @@ RSpec.describe API::Files do
end
end
context 'when unauthenticated', 'and project is public' do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository files' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
@ -342,7 +367,24 @@ RSpec.describe API::Files do
end
end
context 'when PATs are used' do
context 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route(file_path)), params: params }
let(:message) { '404 Project Not Found' }
end
end
end
context 'when authenticated' do
context 'and user is a direct project member' do
context 'and project is private' do
context 'and user is a developer' do
it_behaves_like 'repository files' do
let(:current_user) { user }
let(:api_user) { user }
end
context 'and PATs are used' do
it_behaves_like 'repository files' do
let(:token) { create(:personal_access_token, scopes: ['read_repository'], user: user) }
let(:current_user) { user }
@ -350,27 +392,65 @@ RSpec.describe API::Files do
let(:options) { { personal_access_token: token } }
end
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route(file_path)), params: params }
let(:message) { '404 Project Not Found' }
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository files' do
let(:current_user) { user }
let(:api_user) { user }
end
end
context 'when authenticated', 'as a guest' do
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route(file_path), guest), params: params }
end
end
end
end
end
context 'when authenticated' do
context 'and user is an inherited member from the group' do
context 'when project is public with private repository' do
let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is a guest' do
it_behaves_like 'returns non-executable file attributes as json' do
let(:api_user) { inherited_guest }
end
end
context 'and user is a reporter' do
it_behaves_like 'returns non-executable file attributes as json' do
let(:api_user) { inherited_reporter }
end
end
context 'and user is a developer' do
it_behaves_like 'returns non-executable file attributes as json' do
let(:api_user) { inherited_developer }
end
end
end
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :repository, group: group) }
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route(file_path), inherited_guest), params: params }
end
end
context 'and user is a reporter' do
it_behaves_like 'returns non-executable file attributes as json' do
let(:api_user) { inherited_reporter }
end
end
context 'and user is a developer' do
it_behaves_like 'returns non-executable file attributes as json' do
let(:api_user) { inherited_developer }
end
end
end
end
end
end
describe 'GET /projects/:id/repository/files/:file_path/blame' do
shared_examples_for 'repository blame files' do
@ -406,11 +486,10 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path))
expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb')
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
expect(response.headers['X-Gitlab-Content-Sha256'])
.to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
expect(response.headers['X-Gitlab-Execute-Filemode']).to eq("false")
expect(response.headers['X-Gitlab-File-Name']).to eq(file_name)
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq(last_commit_id)
expect(response.headers['X-Gitlab-Content-Sha256']).to eq(content_sha256)
expect(response.headers['X-Gitlab-Execute-Filemode']).to eq('false')
end
context 'for executable file' do
@ -424,13 +503,13 @@ RSpec.describe API::Files do
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('6b8dc4a827797aa025ff6b8f425e583858a10d4f')
expect(response.headers['X-Gitlab-Content-Sha256'])
.to eq('2c74b1181ef780dfb692c030d3a0df6e0b624135c38a9344e56b9f80007b6191')
expect(response.headers['X-Gitlab-Execute-Filemode']).to eq("true")
expect(response.headers['X-Gitlab-Execute-Filemode']).to eq('true')
end
end
end
it 'returns 400 when file path is invalid' do
get api(route(rouge_file_path) + '/blame', current_user), params: params
get api(route(invalid_file_path) + '/blame', current_user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@ -573,31 +652,35 @@ RSpec.describe API::Files do
end
end
context 'when unauthenticated', 'and project is public' do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository blame files' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
context 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route(file_path)), params: params }
let(:message) { '404 Project Not Found' }
end
end
end
context 'when authenticated', 'as a developer' do
context 'when authenticated' do
context 'and user is a developer' do
it_behaves_like 'repository blame files' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route(file_path) + '/blame', guest), params: params }
end
end
end
context 'when PATs are used' do
it 'returns blame file by commit sha' do
@ -614,10 +697,10 @@ RSpec.describe API::Files do
end
end
describe "GET /projects/:id/repository/files/:file_path/raw" do
describe 'GET /projects/:id/repository/files/:file_path/raw' do
shared_examples_for 'repository raw files' do
it 'returns 400 when file path is invalid' do
get api(route(rouge_file_path) + "/raw", current_user), params: params
get api(route(invalid_file_path) + '/raw', current_user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@ -628,7 +711,7 @@ RSpec.describe API::Files do
end
it 'returns raw file info' do
url = route(file_path) + "/raw"
url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
end
@ -639,39 +722,39 @@ RSpec.describe API::Files do
end
it 'returns response :ok', :aggregate_failures do
url = route(file_path) + "/raw"
url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, current_user), {})
end
end
it 'returns raw file info for files with dots' do
url = route('.gitignore') + "/raw"
url = route('.gitignore') + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
expect_to_send_git_blob(api(route(file_path) + "/raw", current_user), params)
expect_to_send_git_blob(api(route(file_path) + '/raw', current_user), params)
end
it 'sets no-cache headers' do
url = route('.gitignore') + "/raw"
url = route('.gitignore') + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
expect(response.headers['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store, no-cache')
expect(response.headers['Pragma']).to eq('no-cache')
expect(response.headers['Expires']).to eq('Fri, 01 Jan 1990 00:00:00 GMT')
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user) }
let(:request) { get api(route('any%2Ffile'), current_user) }
end
end
@ -693,67 +776,88 @@ RSpec.describe API::Files do
end
end
context 'when unauthenticated', 'and project is public' do
context 'when unauthenticated' do
context 'and project is public' do
it_behaves_like 'repository raw files' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
context 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route(file_path)), params: params }
let(:message) { '404 Project Not Found' }
end
end
end
context 'when authenticated', 'as a developer' do
context 'when authenticated' do
context 'and user is a developer' do
it_behaves_like 'repository raw files' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route(file_path), guest), params: params }
end
end
end
context 'when PATs are used' do
it 'returns file by commit sha' do
token = create(:personal_access_token, scopes: ['read_repository'], user: user)
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
url = api(route(file_path) + "/raw", personal_access_token: token)
file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
url = api(route(file_path) + '/raw', personal_access_token: token)
expect_to_send_git_blob(url, params)
end
end
end
describe "POST /projects/:id/repository/files/:file_path" do
let!(:file_path) { "new_subfolder%2Fnewfile%2Erb" }
describe 'POST /projects/:id/repository/files/:file_path' do
let!(:file_path) { 'new_subfolder%2Fnewfile%2Erb' }
let(:params) do
{
branch: "master",
content: "puts 8",
commit_message: "Added newfile"
branch: 'master',
content: 'puts 8',
commit_message: 'Added newfile'
}
end
let(:executable_params) do
{
branch: "master",
content: "puts 8",
commit_message: "Added newfile",
branch: 'master',
content: 'puts 8',
commit_message: 'Added newfile',
execute_filemode: true
}
end
shared_examples 'creates a new file in the project repo' do
specify do
post api(route(file_path), current_user), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['file_path']).to eq(CGI.unescape(file_path))
last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(current_user.email)
expect(last_commit.author_name).to eq(current_user.name)
expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(false)
end
end
context 'when authenticated', 'as a direct project member' do
context 'when project is private' do
context 'and user is a developer' do
it 'returns 400 when file path is invalid' do
post api(route(rouge_file_path), user), params: params
post api(route(invalid_file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@ -763,53 +867,56 @@ RSpec.describe API::Files do
subject { post api(route(absolute_path), user), params: params }
end
it "creates a new file in project repo" do
post api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(user.email)
expect(last_commit.author_name).to eq(user.name)
expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(false)
it_behaves_like 'creates a new file in the project repo' do
let(:current_user) { user }
end
it "creates a new executable file in project repo" do
it 'creates a new executable file in project repo' do
post api(route(file_path), user), params: executable_params
expect(response).to have_gitlab_http_status(:created)
expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
expect(json_response['file_path']).to eq(CGI.unescape(file_path))
last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(user.email)
expect(last_commit.author_name).to eq(user.name)
expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(true)
end
it "returns a 400 bad request if no mandatory params given" do
post api(route("any%2Etxt"), user)
context 'when no mandatory params given' do
it 'returns a 400 bad request' do
post api(route('any%2Etxt'), user)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it 'returns a 400 bad request if the commit message is empty' do
context 'when the commit message is empty' do
before do
params[:commit_message] = ''
end
it 'returns a 400 bad request' do
post api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it "returns a 400 if editor fails to create file" do
context 'when editor fails to create file' do
before do
allow_next_instance_of(Repository) do |instance|
allow(instance).to receive(:create_file).and_raise(Gitlab::Git::CommitError, 'Cannot create file')
end
end
post api(route("any%2Etxt"), user), params: params
it 'returns a 400 bad request' do
post api(route('any%2Etxt'), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'with PATs' do
context 'and PATs are used' do
it 'returns 403 with `read_repository` scope' do
token = create(:personal_access_token, scopes: ['read_repository'], user: user)
@ -827,11 +934,20 @@ RSpec.describe API::Files do
end
end
context "when specifying an author" do
it "creates a new file with the specified author" do
context 'and the repo is empty' do
let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
it_behaves_like 'creates a new file in the project repo' do
let(:current_user) { user }
let(:file_path) { 'newfile%2Erb' }
end
end
context 'when specifying an author' do
it 'creates a new file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name)
post api(route("new_file_with_author%2Etxt"), user), params: params
post api(route('new_file_with_author%2Etxt'), user), params: params
expect(response).to have_gitlab_http_status(:created)
expect(response.media_type).to eq('application/json')
@ -840,23 +956,60 @@ RSpec.describe API::Files do
expect(last_commit.author_name).to eq(author_name)
end
end
context 'when the repo is empty' do
let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
it "creates a new file in project repo" do
post api(route("newfile%2Erb"), user), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['file_path']).to eq('newfile.rb')
last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(user.email)
expect(last_commit.author_name).to eq(user.name)
end
end
end
describe "PUT /projects/:id/repository/files" do
context 'when authenticated' do
context 'and user is an inherited member from the group' do
context 'when project is public with private repository' do
let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { post api(route(file_path), inherited_guest), params: params }
end
end
context 'and user is a reporter' do
it_behaves_like '403 response' do
let(:request) { post api(route(file_path), inherited_reporter), params: params }
end
end
context 'and user is a developer' do
it_behaves_like 'creates a new file in the project repo' do
let(:current_user) { inherited_developer }
end
end
end
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :repository, group: group) }
context 'and user is a guest' do
it_behaves_like '403 response' do
let(:request) { post api(route(file_path), inherited_guest), params: params }
end
end
context 'and user is a reporter' do
it_behaves_like '403 response' do
let(:request) { post api(route(file_path), inherited_reporter), params: params }
end
end
context 'and user is a developer' do
it_behaves_like 'creates a new file in the project repo' do
let(:current_user) { inherited_developer }
end
end
end
end
end
end
describe 'PUT /projects/:id/repository/files' do
let(:params) do
{
branch: 'master',
@ -865,7 +1018,7 @@ RSpec.describe API::Files do
}
end
it "updates existing file in project repo" do
it 'updates existing file in project repo' do
put api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:ok)
@ -875,43 +1028,59 @@ RSpec.describe API::Files do
expect(last_commit.author_name).to eq(user.name)
end
it 'returns a 400 bad request if the commit message is empty' do
context 'when the commit message is empty' do
before do
params[:commit_message] = ''
end
it 'returns a 400 bad request' do
put api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it "returns a 400 bad request if update existing file with stale last commit id" do
params_with_stale_id = params.merge(last_commit_id: 'stale')
context 'when updating an existing file with stale last commit id' do
let(:params_with_stale_id) { params.merge(last_commit_id: 'stale') }
it 'returns a 400 bad request' do
put api(route(file_path), user), params: params_with_stale_id
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(_('You are attempting to update a file that has changed since you started editing it.'))
end
end
it "updates existing file in project repo with accepts correct last commit id" do
last_commit = Gitlab::Git::Commit
context 'with correct last commit id' do
let(:last_commit) do
Gitlab::Git::Commit
.last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
params_with_correct_id = params.merge(last_commit_id: last_commit.id)
end
let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
it 'updates existing file in project repo' do
put api(route(file_path), user), params: params_with_correct_id
expect(response).to have_gitlab_http_status(:ok)
end
end
it "returns 400 when file path is invalid" do
last_commit = Gitlab::Git::Commit
context 'when file path is invalid' do
let(:last_commit) do
Gitlab::Git::Commit
.last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
params_with_correct_id = params.merge(last_commit_id: last_commit.id)
end
put api(route(rouge_file_path), user), params: params_with_correct_id
let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
it 'returns a 400 bad request' do
put api(route(invalid_file_path), user), params: params_with_correct_id
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
end
end
it_behaves_like 'when path is absolute' do
let(:last_commit) do
@ -924,15 +1093,17 @@ RSpec.describe API::Files do
subject { put api(route(absolute_path), user), params: params_with_correct_id }
end
it "returns a 400 bad request if no params given" do
context 'when no params given' do
it 'returns a 400 bad request' do
put api(route(file_path), user)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context "when specifying an author" do
it "updates a file with the specified author" do
params.merge!(author_email: author_email, author_name: author_name, content: "New content")
context 'when specifying an author' do
it 'updates a file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name, content: 'New content')
put api(route(file_path), user), params: params
@ -982,7 +1153,7 @@ RSpec.describe API::Files do
end
end
describe "DELETE /projects/:id/repository/files" do
describe 'DELETE /projects/:id/repository/files' do
let(:params) do
{
branch: 'master',
@ -991,7 +1162,7 @@ RSpec.describe API::Files do
end
it 'returns 400 when file path is invalid' do
delete api(route(rouge_file_path), user), params: params
delete api(route(invalid_file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@ -1001,38 +1172,48 @@ RSpec.describe API::Files do
subject { delete api(route(absolute_path), user), params: params }
end
it "deletes existing file in project repo" do
it 'deletes existing file in project repo' do
delete api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:no_content)
end
it "returns a 400 bad request if no params given" do
context 'when no params given' do
it 'returns a 400 bad request' do
delete api(route(file_path), user)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it 'returns a 400 bad request if the commit message is empty' do
context 'when the commit message is empty' do
before do
params[:commit_message] = ''
end
it 'returns a 400 bad request' do
delete api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
end
it "returns a 400 if fails to delete file" do
context 'when fails to delete file' do
before do
allow_next_instance_of(Repository) do |instance|
allow(instance).to receive(:delete_file).and_raise(Gitlab::Git::CommitError, 'Cannot delete file')
end
end
it 'returns a 400 bad request' do
delete api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context "when specifying an author" do
it "removes a file with the specified author" do
context 'when specifying an author' do
it 'removes a file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name)
delete api(route(file_path), user), params: params
@ -1042,7 +1223,7 @@ RSpec.describe API::Files do
end
end
describe "POST /projects/:id/repository/files with binary file" do
describe 'POST /projects/:id/repository/files with binary file' do
let(:file_path) { 'test%2Ebin' }
let(:put_params) do
{
@ -1063,7 +1244,7 @@ RSpec.describe API::Files do
post api(route(file_path), user), params: put_params
end
it "remains unchanged" do
it 'remains unchanged' do
get api(route(file_path), user), params: get_params
expect(response).to have_gitlab_http_status(:ok)

View File

@ -5,26 +5,18 @@ require 'spec_helper'
RSpec.describe 'Creation of a new branch' do
include GraphqlHelpers
let_it_be(:group) { create(:group, :public) }
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project, :public, :empty_repo) }
let(:input) { { project_path: project.full_path, name: new_branch, ref: ref } }
let(:new_branch) { 'new_branch' }
let(:new_branch) { "new_branch_#{SecureRandom.hex(4)}" }
let(:ref) { 'master' }
let(:mutation) { graphql_mutation(:create_branch, input) }
let(:mutation_response) { graphql_mutation_response(:create_branch) }
context 'the user is not allowed to create a branch' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when user has permissions to create a branch' do
before do
project.add_developer(current_user)
end
it 'creates a new branch' do
shared_examples 'creates a new branch' do
specify do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
@ -33,14 +25,75 @@ RSpec.describe 'Creation of a new branch' do
'commit' => a_hash_including('id')
)
end
end
context 'when project is public' do
let_it_be(:project) { create(:project, :public, :empty_repo) }
context 'when user is not allowed to create a branch' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when user is a direct project member' do
context 'and user is a developer' do
before do
project.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
context 'when ref is not correct' do
err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\''
let(:new_branch) { 'another_branch' }
let(:ref) { 'unknown' }
it_behaves_like 'a mutation that returns errors in the response',
errors: [err_msg]
it_behaves_like 'a mutation that returns errors in the response', errors: [err_msg]
end
end
end
context 'when user is an inherited member from the group' do
context 'when project has a private repository' do
let_it_be(:project) { create(:project, :public, :empty_repo, :repository_private, group: group) }
context 'and user is a guest' do
before do
group.add_guest(current_user)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'and user is a developer' do
before do
group.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
end
end
end
end
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :empty_repo, group: group) }
context 'when user is an inherited member from the group' do
context 'and user is a guest' do
before do
group.add_guest(current_user)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'and user is a developer' do
before do
group.add_developer(current_user)
end
it_behaves_like 'creates a new branch'
end
end
end
end

View File

@ -923,6 +923,16 @@ RSpec.describe API::Groups do
expect(json_response['prevent_sharing_groups_outside_hierarchy']).to eq(true)
end
it 'removes the group avatar' do
put api("/groups/#{group1.id}", user1), params: { avatar: '' }
aggregate_failures "testing response" do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['avatar_url']).to be_nil
expect(group1.reload.avatar_url).to be_nil
end
end
it 'does not update visibility_level if it is restricted' do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])

View File

@ -573,6 +573,224 @@ RSpec.describe API::Releases do
end
end
describe 'GET /projects/:id/releases/:tag_name/downloads/*file_path' do
let!(:release) { create(:release, project: project, tag: 'v0.1', author: maintainer) }
let!(:link) { create(:release_link, release: release, url: "#{url}#{filepath}", filepath: filepath) }
let(:filepath) { '/bin/bigfile.exe' }
let(:url) { 'https://google.com/-/jobs/140463678/artifacts/download' }
context 'with an invalid release tag' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Not Found')
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", nil)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Project Not Found')
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'with a valid release tag' do
context 'when filepath is provided' do
context 'when filepath exists' do
it 'redirects to the file download URL' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", maintainer)
expect(response).to redirect_to("#{url}#{filepath}")
end
it 'redirects to the file download URL when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}"), params: { job_token: job.token }
expect(response).to redirect_to("#{url}#{filepath}")
end
context 'when user is a guest' do
it 'responds 403 Forbidden' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'when project is public' do
let(:project) { create(:project, :repository, :public) }
it 'responds 200 OK' do
get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
expect(response).to redirect_to("#{url}#{filepath}")
end
end
end
end
context 'when filepath does not exists' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Not found')
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", nil)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Project Not Found')
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
context 'when filepath is not provided' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.1/downloads", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns project not found for no user' do
get api("/projects/#{project.id}/releases/v0.1/downloads", nil)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns forbidden for guest' do
get api("/projects/#{project.id}/releases/v0.1/downloads", guest)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe 'GET /projects/:id/releases/permalink/latest' do
context 'when there is no release' do
it 'returns not found' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns not found when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when there are more than one release' do
let!(:release_a) do
create(:release,
project: project,
tag: 'v0.1',
author: maintainer,
description: 'This is v0.1',
released_at: 3.days.ago)
end
let!(:release_b) do
create(:release,
project: project,
tag: 'v0.2',
author: maintainer,
description: 'This is v0.2',
released_at: 2.days.ago)
end
it 'redirects to the latest release tag' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
end
it 'redirects to the latest release tag when using JOB-TOKEN auth' do
job = create(:ci_build, :running, project: project, user: maintainer)
get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
end
context 'when there are query parameters present' do
it 'includes the query params on the redirection' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { include_html_description: true, other_param: "aaa" }
uri = URI(response.header["Location"])
query_params = Rack::Utils.parse_nested_query(uri.query)
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
expect(query_params).to include({
"include_html_description" => "true",
"other_param" => "aaa"
})
end
it 'discards the `order_by` query param' do
get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { order_by: 'something', other_param: "aaa" }
uri = URI(response.header["Location"])
query_params = Rack::Utils.parse_nested_query(uri.query)
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
expect(query_params).to include({
"other_param" => "aaa"
})
expect(query_params).not_to include({
"order_by" => "something"
})
end
end
context 'when downloading a release asset' do
it 'redirects to the right endpoint keeping the suffix_path' do
get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/example.exe", maintainer)
uri = URI(response.header["Location"])
expect(response).to have_gitlab_http_status(:redirect)
expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}/downloads/bin/example.exe")
end
it 'returns error when there is path traversal in suffix path' do
get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/../../../../../../../password.txt", maintainer)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('suffix_path should be a valid file path')
end
end
end
end
describe 'POST /projects/:id/releases' do
let(:params) do
{

View File

@ -201,6 +201,25 @@ RSpec.describe Environments::StopService do
project.add_developer(user)
end
context 'and merge request has associated created_environments' do
let!(:environment1) { create(:environment, project: project, merge_request: merge_request) }
let!(:environment2) { create(:environment, project: project, merge_request: merge_request) }
before do
subject
end
it 'stops the associated created_environments' do
expect(environment1.reload).to be_stopped
expect(environment2.reload).to be_stopped
end
it 'does not affect environments that are not associated to the merge request' do
expect(pipeline.environments_in_self_and_project_descendants.first.merge_request).to be_nil
expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
end
it 'stops the active environment' do
subject
expect(pipeline.environments_in_self_and_project_descendants.first).to be_stopping

View File

@ -218,7 +218,6 @@
- './ee/spec/elastic/migrate/20220713103500_delete_commits_from_original_index_spec.rb'
- './ee/spec/factories/lfs_object_spec.rb'
- './ee/spec/features/account_recovery_regular_check_spec.rb'
- './ee/spec/features/admin/admin_audit_logs_spec.rb'
- './ee/spec/features/admin/admin_credentials_inventory_spec.rb'
- './ee/spec/features/admin/admin_dashboard_spec.rb'
- './ee/spec/features/admin/admin_dev_ops_reports_spec.rb'