Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
66d4203791
commit
17ab40ca08
|
@ -50,10 +50,6 @@ After your merge request has being approved according to our [approval guideline
|
|||
| -------- | -------- |
|
||||
| Issue on [GitLab](https://gitlab.com/gitlab-org/gitlab/issues) | #TODO |
|
||||
| Security Release tracking issue | #TODO |
|
||||
| `master` MR | !TODO |
|
||||
| `Backport X.Y` MR | !TODO |
|
||||
| `Backport X.Y` MR | !TODO |
|
||||
| `Backport X.Y` MR | !TODO |
|
||||
|
||||
### Details
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
|
|||
|
||||
## Related issues
|
||||
|
||||
<!-- Mention the issue(s) this MR is related to -->
|
||||
<!-- Mention the GitLab Security issue this MR is related to -->
|
||||
|
||||
## Developer checklist
|
||||
|
||||
- [ ] Link this MR in the `links` section of the related issue on [GitLab Security].
|
||||
- [ ] **Make sure this merge request mentions the [GitLab Security] issue it belongs to (i.e. `Related to <issue_id>`).**
|
||||
- [ ] Merge request targets `master`, or `X-Y-stable` for backports.
|
||||
- [ ] Milestone is set for the version this merge request applies to. A closed milestone can be assigned via [quick actions].
|
||||
- [ ] Title of this merge request is the same as for all backports.
|
||||
|
|
|
@ -349,8 +349,8 @@ RSpec/HaveGitlabHttpStatus:
|
|||
- 'ee/spec/requests/{groups,projects,repositories}/**/*'
|
||||
- 'spec/requests/api/*/**/*.rb'
|
||||
- 'ee/spec/requests/api/*/**/*.rb'
|
||||
- 'spec/requests/api/[a-l]*.rb'
|
||||
- 'ee/spec/requests/api/[a-l]*.rb'
|
||||
- 'spec/requests/api/[a-o]*.rb'
|
||||
- 'ee/spec/requests/api/[a-o]*.rb'
|
||||
|
||||
Style/MultilineWhenThen:
|
||||
Enabled: false
|
||||
|
|
|
@ -44,6 +44,7 @@ class DueDateSelect {
|
|||
this.$selectbox.hide();
|
||||
this.$value.css('display', '');
|
||||
},
|
||||
shouldPropagate: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import { __ } from '../../locale';
|
||||
import Icon from '../../vue_shared/components/icon.vue';
|
||||
import getRefMixin from '../mixins/get_ref';
|
||||
|
@ -102,12 +103,12 @@ export default {
|
|||
.filter(p => p !== '')
|
||||
.reduce(
|
||||
(acc, name, i) => {
|
||||
const path = `${i > 0 ? acc[i].path : ''}/${name}`;
|
||||
const path = joinPaths(i > 0 ? acc[i].path : '', encodeURIComponent(name));
|
||||
|
||||
return acc.concat({
|
||||
name,
|
||||
path,
|
||||
to: `/-/tree/${escape(this.ref)}${escape(path)}`,
|
||||
to: `/-/tree/${joinPaths(escape(this.ref), path)}`,
|
||||
});
|
||||
},
|
||||
[
|
||||
|
|
|
@ -91,7 +91,9 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
routerLinkTo() {
|
||||
return this.isFolder ? { path: `/-/tree/${escape(this.ref)}/${escape(this.path)}` } : null;
|
||||
return this.isFolder
|
||||
? { path: `/-/tree/${escape(this.ref)}/${encodeURIComponent(this.path)}` }
|
||||
: null;
|
||||
},
|
||||
iconName() {
|
||||
return `fa-${getIconName(this.type, this.path)}`;
|
||||
|
@ -141,6 +143,7 @@ export default {
|
|||
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
|
||||
<component
|
||||
:is="linkComponent"
|
||||
ref="link"
|
||||
:to="routerLinkTo"
|
||||
:href="url"
|
||||
class="str-truncated"
|
||||
|
|
|
@ -27,7 +27,7 @@ export function fetchLogsTree(client, path, offset, resolver = null) {
|
|||
|
||||
fetchpromise = axios
|
||||
.get(
|
||||
`${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${escape(
|
||||
`${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${encodeURIComponent(
|
||||
path.replace(/^\//, ''),
|
||||
)}`,
|
||||
{
|
||||
|
|
|
@ -3,8 +3,8 @@ import { s__ } from '~/locale';
|
|||
export const PAGINATION_UI_BUTTON_LIMIT = 4;
|
||||
export const UI_LIMIT = 6;
|
||||
export const SPREAD = '...';
|
||||
export const PREV = s__('Pagination|‹ Prev');
|
||||
export const NEXT = s__('Pagination|Next ›');
|
||||
export const PREV = s__('Pagination|Prev');
|
||||
export const NEXT = s__('Pagination|Next');
|
||||
export const FIRST = s__('Pagination|« First');
|
||||
export const LAST = s__('Pagination|Last »');
|
||||
export const LABEL_FIRST_PAGE = s__('Pagination|Go to first page');
|
||||
|
|
|
@ -22,6 +22,16 @@ module Projects
|
|||
end
|
||||
end
|
||||
|
||||
def update
|
||||
result = ::Metrics::Dashboard::UpdateDashboardService.new(project, current_user, dashboard_params.merge(file_content_params)).execute
|
||||
|
||||
if result[:status] == :success
|
||||
respond_update_success(result)
|
||||
else
|
||||
respond_error(result)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def respond_success(result)
|
||||
|
@ -43,6 +53,19 @@ module Projects
|
|||
flash[:notice] = message.html_safe
|
||||
end
|
||||
|
||||
def respond_update_success(result)
|
||||
set_web_ide_link_update_notice(result.dig(:dashboard, :path))
|
||||
respond_to do |format|
|
||||
format.json { render status: result.delete(:http_status), json: result }
|
||||
end
|
||||
end
|
||||
|
||||
def set_web_ide_link_update_notice(new_dashboard_path)
|
||||
web_ide_link_start = "<a href=\"#{ide_edit_path(project, redirect_safe_branch_name, new_dashboard_path)}\">"
|
||||
message = _("Your dashboard has been updated. You can %{web_ide_link_start}edit it here%{web_ide_link_end}.") % { web_ide_link_start: web_ide_link_start, web_ide_link_end: "</a>" }
|
||||
flash[:notice] = message.html_safe
|
||||
end
|
||||
|
||||
def validate_required_params!
|
||||
params.require(%i(branch file_name dashboard commit_message))
|
||||
end
|
||||
|
@ -54,6 +77,31 @@ module Projects
|
|||
def dashboard_params
|
||||
params.permit(%i(branch file_name dashboard commit_message)).to_h
|
||||
end
|
||||
|
||||
def file_content_params
|
||||
params.permit(
|
||||
file_content: [
|
||||
:dashboard,
|
||||
panel_groups: [
|
||||
:group,
|
||||
:priority,
|
||||
panels: [
|
||||
:type,
|
||||
:title,
|
||||
:y_label,
|
||||
:weight,
|
||||
metrics: [
|
||||
:id,
|
||||
:unit,
|
||||
:label,
|
||||
:query,
|
||||
:query_range
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -90,6 +90,12 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def needs_attributes
|
||||
strong_memoize(:needs_attributes) do
|
||||
needs.map { |need| need.attributes.except('id', 'build_id') }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_scheduling_type?
|
||||
|
|
|
@ -52,6 +52,13 @@ module ReactiveCaching
|
|||
end
|
||||
end
|
||||
|
||||
def with_reactive_cache_set(resource, opts, &blk)
|
||||
data = with_reactive_cache(resource, opts, &blk)
|
||||
save_keys_in_set(resource, opts) if data
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
# This method is used for debugging purposes and should not be used otherwise.
|
||||
def without_reactive_cache(*args, &blk)
|
||||
return with_reactive_cache(*args, &blk) unless Rails.env.development?
|
||||
|
@ -65,6 +72,12 @@ module ReactiveCaching
|
|||
Rails.cache.delete(alive_reactive_cache_key(*args))
|
||||
end
|
||||
|
||||
def clear_reactive_cache_set!(*args)
|
||||
cache_key = full_reactive_cache_key(args)
|
||||
|
||||
reactive_set_cache.clear_cache!(cache_key)
|
||||
end
|
||||
|
||||
def exclusively_update_reactive_cache!(*args)
|
||||
locking_reactive_cache(*args) do
|
||||
key = full_reactive_cache_key(*args)
|
||||
|
@ -86,6 +99,16 @@ module ReactiveCaching
|
|||
|
||||
private
|
||||
|
||||
def save_keys_in_set(resource, opts)
|
||||
cache_key = full_reactive_cache_key(resource)
|
||||
|
||||
reactive_set_cache.write(cache_key, "#{cache_key}:#{opts}")
|
||||
end
|
||||
|
||||
def reactive_set_cache
|
||||
Gitlab::ReactiveCacheSetCache.new(expires_in: reactive_cache_lifetime)
|
||||
end
|
||||
|
||||
def refresh_reactive_cache!(*args)
|
||||
clear_reactive_cache!(*args)
|
||||
keep_alive_reactive_cache!(*args)
|
||||
|
|
|
@ -85,7 +85,7 @@ module ErrorTracking
|
|||
end
|
||||
|
||||
def list_sentry_issues(opts = {})
|
||||
with_reactive_cache('list_issues', opts.stringify_keys) do |result|
|
||||
with_reactive_cache_set('list_issues', opts.stringify_keys) do |result|
|
||||
result
|
||||
end
|
||||
end
|
||||
|
@ -130,6 +130,10 @@ module ErrorTracking
|
|||
end
|
||||
end
|
||||
|
||||
def expire_issues_cache
|
||||
clear_reactive_cache_set!('list_issues')
|
||||
end
|
||||
|
||||
# http://HOST/api/0/projects/ORG/PROJECT
|
||||
# ->
|
||||
# http://HOST/ORG/PROJECT
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
class CreateJobArtifactsService
|
||||
class CreateJobArtifactsService < ::BaseService
|
||||
ArtifactsExistError = Class.new(StandardError)
|
||||
OBJECT_STORAGE_ERRORS = [
|
||||
Errno::EIO,
|
||||
Google::Apis::ServerError,
|
||||
Signet::RemoteServerError
|
||||
].freeze
|
||||
|
||||
def execute(job, artifacts_file, params, metadata_file: nil)
|
||||
expire_in = params['expire_in'] ||
|
||||
|
@ -26,18 +31,20 @@ module Ci
|
|||
expire_in: expire_in)
|
||||
end
|
||||
|
||||
job.update(artifacts_expire_in: expire_in)
|
||||
if job.update(artifacts_expire_in: expire_in)
|
||||
success
|
||||
else
|
||||
error(job.errors.messages, :bad_request)
|
||||
end
|
||||
|
||||
rescue ActiveRecord::RecordNotUnique => error
|
||||
return true if sha256_matches_existing_artifact?(job, params['artifact_type'], artifacts_file)
|
||||
return success if sha256_matches_existing_artifact?(job, params['artifact_type'], artifacts_file)
|
||||
|
||||
Gitlab::ErrorTracking.track_exception(error,
|
||||
job_id: job.id,
|
||||
project_id: job.project_id,
|
||||
uploading_type: params['artifact_type']
|
||||
)
|
||||
|
||||
job.errors.add(:base, 'another artifact of the same type already exists')
|
||||
false
|
||||
track_exception(error, job, params)
|
||||
error('another artifact of the same type already exists', :bad_request)
|
||||
rescue *OBJECT_STORAGE_ERRORS => error
|
||||
track_exception(error, job, params)
|
||||
error(error.message, :service_unavailable)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -48,5 +55,13 @@ module Ci
|
|||
|
||||
existing_artifact.file_sha256 == artifacts_file.sha256
|
||||
end
|
||||
|
||||
def track_exception(error, job, params)
|
||||
Gitlab::ErrorTracking.track_exception(error,
|
||||
job_id: job.id,
|
||||
project_id: job.project_id,
|
||||
uploading_type: params['artifact_type']
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,8 @@ module Ci
|
|||
CLONE_ACCESSORS = %i[pipeline project ref tag options name
|
||||
allow_failure stage stage_id stage_idx trigger_request
|
||||
yaml_variables when environment coverage_regex
|
||||
description tag_list protected needs resource_group scheduling_type].freeze
|
||||
description tag_list protected needs_attributes
|
||||
resource_group scheduling_type].freeze
|
||||
|
||||
def execute(build)
|
||||
reprocess!(build).tap do |new_build|
|
||||
|
|
|
@ -11,6 +11,7 @@ module ErrorTracking
|
|||
)
|
||||
|
||||
compose_response(response) do
|
||||
project_error_tracking_setting.expire_issues_cache
|
||||
response[:closed_issue_iid] = update_related_issue&.iid
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Updates the content of a specified dashboard in .yml file inside `.gitlab/dashboards`
|
||||
module Metrics
|
||||
module Dashboard
|
||||
class UpdateDashboardService < ::BaseService
|
||||
ALLOWED_FILE_TYPE = '.yml'
|
||||
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::ProjectDashboardService::DASHBOARD_ROOT
|
||||
|
||||
def execute
|
||||
catch(:error) do
|
||||
throw(:error, error(_(%q(You can't commit to this project)), :forbidden)) unless push_authorized?
|
||||
|
||||
result = ::Files::UpdateService.new(project, current_user, dashboard_attrs).execute
|
||||
throw(:error, result.merge(http_status: :bad_request)) unless result[:status] == :success
|
||||
|
||||
success(result.merge(http_status: :created, dashboard: dashboard_details))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dashboard_attrs
|
||||
{
|
||||
commit_message: params[:commit_message],
|
||||
file_path: update_dashboard_path,
|
||||
file_content: update_dashboard_content,
|
||||
encoding: 'text',
|
||||
branch_name: branch,
|
||||
start_branch: repository.branch_exists?(branch) ? branch : project.default_branch
|
||||
}
|
||||
end
|
||||
|
||||
def dashboard_details
|
||||
{
|
||||
path: update_dashboard_path,
|
||||
display_name: ::Metrics::Dashboard::ProjectDashboardService.name_for_path(update_dashboard_path),
|
||||
default: false,
|
||||
system_dashboard: false
|
||||
}
|
||||
end
|
||||
|
||||
def push_authorized?
|
||||
Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch)
|
||||
end
|
||||
|
||||
def branch
|
||||
@branch ||= begin
|
||||
throw(:error, error(_('There was an error updating the dashboard, branch name is invalid.'), :bad_request)) unless valid_branch_name?
|
||||
throw(:error, error(_('There was an error updating the dashboard, branch named: %{branch} already exists.') % { branch: params[:branch] }, :bad_request)) unless new_or_default_branch?
|
||||
|
||||
params[:branch]
|
||||
end
|
||||
end
|
||||
|
||||
def new_or_default_branch?
|
||||
!repository.branch_exists?(params[:branch]) || project.default_branch == params[:branch]
|
||||
end
|
||||
|
||||
def valid_branch_name?
|
||||
Gitlab::GitRefValidator.validate(params[:branch])
|
||||
end
|
||||
|
||||
def update_dashboard_path
|
||||
File.join(USER_DASHBOARDS_DIR, file_name)
|
||||
end
|
||||
|
||||
def target_file_type_valid?
|
||||
File.extname(params[:file_name]) == ALLOWED_FILE_TYPE
|
||||
end
|
||||
|
||||
def file_name
|
||||
@file_name ||= begin
|
||||
throw(:error, error(_('The file name should have a .yml extension'), :bad_request)) unless target_file_type_valid?
|
||||
|
||||
File.basename(CGI.unescape(params[:file_name]))
|
||||
end
|
||||
end
|
||||
|
||||
def update_dashboard_content
|
||||
::PerformanceMonitoring::PrometheusDashboard.from_json(params[:file_content]).to_yaml
|
||||
end
|
||||
|
||||
def repository
|
||||
@repository ||= project.repository
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Added the multiSelect option to stop event propagation when clicking on the
|
||||
dropdown
|
||||
merge_request: 24611
|
||||
author: Gwen_
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Don't show issue as blocked on the issue board if blocking issue is closed
|
||||
merge_request: 25817
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Keep needs association on the retried build
|
||||
merge_request: 25888
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update file content of an existing custom dashboard
|
||||
merge_request: 25024
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Return 503 to the Runner when the object storage is unavailable
|
||||
merge_request: 25822
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed repository browsing for folders with non-ascii characters
|
||||
merge_request: 25877
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove special chars from previous and next items in pagination
|
||||
merge_request: 25891
|
||||
author:
|
||||
type: other
|
|
@ -252,7 +252,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
end
|
||||
|
||||
namespace :performance_monitoring do
|
||||
resources :dashboards, only: [:create]
|
||||
resources :dashboards, only: [:create] do
|
||||
collection do
|
||||
put '/:file_name', to: 'dashboards#update', constraints: { file_name: /.+\.yml/ }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :error_tracking do
|
||||
|
|
|
@ -85,13 +85,27 @@ the following feature flags are enabled on your GitLab instance:
|
|||
- `:ci_use_merge_request_ref`
|
||||
- `:merge_ref_auto_sync`
|
||||
|
||||
To check these feature flag values, please ask administrator to execute the following commands:
|
||||
To check and set these feature flag values, please ask an administrator to:
|
||||
|
||||
```shell
|
||||
> sudo gitlab-rails console # Login to Rails console of GitLab instance.
|
||||
> Feature.enabled?(:ci_use_merge_request_ref) # Check if it's enabled or not.
|
||||
> Feature.enable(:ci_use_merge_request_ref) # Enable the feature flag.
|
||||
```
|
||||
1. Log into the Rails console of the GitLab instance:
|
||||
|
||||
```shell
|
||||
sudo gitlab-rails console
|
||||
```
|
||||
|
||||
1. Check if the flags are enabled or not:
|
||||
|
||||
```ruby
|
||||
Feature.enabled?(:ci_use_merge_request_ref)
|
||||
Feature.enabled?(:merge_ref_auto_sync)
|
||||
```
|
||||
|
||||
1. If needed, enable the feature flags:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:ci_use_merge_request_ref)
|
||||
Feature.enable(:merge_ref_auto_sync)
|
||||
```
|
||||
|
||||
### Intermittently pipelines fail by `fatal: reference is not a tree:` error
|
||||
|
||||
|
|
|
@ -283,10 +283,12 @@ module API
|
|||
bad_request!('Missing artifacts file!') unless artifacts
|
||||
file_too_large! unless artifacts.size < max_artifacts_size(job)
|
||||
|
||||
if Ci::CreateJobArtifactsService.new.execute(job, artifacts, params, metadata_file: metadata)
|
||||
result = Ci::CreateJobArtifactsService.new(job.project).execute(job, artifacts, params, metadata_file: metadata)
|
||||
|
||||
if result[:status] == :success
|
||||
status :created
|
||||
else
|
||||
render_validation_error!(job)
|
||||
render_api_error!(result[:message], result[:http_status])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Gitlab
|
||||
module MarkdownCache
|
||||
# Increment this number every time the renderer changes its output
|
||||
CACHE_COMMONMARK_VERSION = 19
|
||||
CACHE_COMMONMARK_VERSION = 20
|
||||
CACHE_COMMONMARK_VERSION_START = 10
|
||||
|
||||
BaseError = Class.new(StandardError)
|
||||
|
|
|
@ -10,9 +10,13 @@ module Gitlab
|
|||
@expires_in = expires_in
|
||||
end
|
||||
|
||||
def cache_key(key)
|
||||
"#{cache_type}:#{key}:set"
|
||||
end
|
||||
|
||||
def clear_cache!(key)
|
||||
with do |redis|
|
||||
keys = read(key).map { |value| "#{cache_type}#{value}" }
|
||||
keys = read(key).map { |value| "#{cache_type}:#{value}" }
|
||||
keys << cache_key(key)
|
||||
|
||||
redis.pipelined do
|
||||
|
@ -24,7 +28,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
def cache_type
|
||||
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:"
|
||||
Gitlab::Redis::Cache::CACHE_NAMESPACE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def entry_path(entry)
|
||||
File.join(*[path, entry[:file_name]].compact)
|
||||
File.join(*[path, entry[:file_name]].compact).force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
|
||||
def build_entry(entry)
|
||||
|
|
|
@ -13658,18 +13658,12 @@ msgstr ""
|
|||
msgid "Pagination|Next"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pagination|Next ›"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pagination|Prev"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pagination|« First"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pagination|‹ Prev"
|
||||
msgstr ""
|
||||
|
||||
msgid "Parameter"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18615,6 +18609,9 @@ msgstr ""
|
|||
msgid "Subscription deletion failed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Subscription successfully applied to \"%{group_name}\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Subscription successfully created."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19633,6 +19630,12 @@ msgstr ""
|
|||
msgid "There was an error trying to validate your query"
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error updating the dashboard, branch name is invalid."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error updating the dashboard, branch named: %{branch} already exists."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error when reseting email token."
|
||||
msgstr ""
|
||||
|
||||
|
@ -22692,6 +22695,9 @@ msgstr ""
|
|||
msgid "Your dashboard has been copied. You can %{web_ide_link_start}edit it here%{web_ide_link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your dashboard has been updated. You can %{web_ide_link_start}edit it here%{web_ide_link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your deployment services will be broken, you will need to manually fix the services after renaming."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -129,4 +129,130 @@ describe Projects::PerformanceMonitoring::DashboardsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'authenticated user' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
let(:file_content) do
|
||||
{
|
||||
"dashboard" => "Dashboard Title",
|
||||
"panel_groups" => [{
|
||||
"group" => "Group Title",
|
||||
"panels" => [{
|
||||
"type" => "area-chart",
|
||||
"title" => "Chart Title",
|
||||
"y_label" => "Y-Axis",
|
||||
"metrics" => [{
|
||||
"id" => "metric_of_ages",
|
||||
"unit" => "count",
|
||||
"label" => "Metric of Ages",
|
||||
"query_range" => "http_requests_total"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
end
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
namespace_id: namespace,
|
||||
project_id: project,
|
||||
dashboard: dashboard,
|
||||
file_name: file_name,
|
||||
file_content: file_content,
|
||||
commit_message: commit_message,
|
||||
branch: branch_name,
|
||||
format: :json
|
||||
}
|
||||
end
|
||||
|
||||
context 'project with repository feature' do
|
||||
context 'with rights to push to the repository' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'valid parameters' do
|
||||
context 'request format json' do
|
||||
let(:update_dashboard_service_params) { params.except(:namespace_id, :project_id, :format) }
|
||||
|
||||
let(:update_dashboard_service_results) do
|
||||
{
|
||||
status: :success,
|
||||
http_status: :created,
|
||||
dashboard: {
|
||||
path: ".gitlab/dashboards/custom_dashboard.yml",
|
||||
display_name: "custom_dashboard.yml",
|
||||
default: false,
|
||||
system_dashboard: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:update_dashboard_service) { instance_double(::Metrics::Dashboard::UpdateDashboardService, execute: update_dashboard_service_results) }
|
||||
|
||||
it 'returns path to new file' do
|
||||
allow(controller).to receive(:repository).and_return(repository)
|
||||
allow(repository).to receive(:find_branch).and_return(branch)
|
||||
allow(::Metrics::Dashboard::UpdateDashboardService).to receive(:new).with(project, user, update_dashboard_service_params).and_return(update_dashboard_service)
|
||||
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status :created
|
||||
expect(response).to set_flash[:notice].to eq("Your dashboard has been updated. You can <a href=\"/-/ide/project/#{namespace.path}/#{project.name}/edit/#{branch_name}/-/.gitlab/dashboards/#{file_name}\">edit it here</a>.")
|
||||
expect(json_response).to eq('status' => 'success', 'dashboard' => { 'default' => false, 'display_name' => "custom_dashboard.yml", 'path' => ".gitlab/dashboards/#{file_name}", 'system_dashboard' => false })
|
||||
end
|
||||
|
||||
context 'UpdateDashboardService failure' do
|
||||
it 'returns json with failure message' do
|
||||
allow(::Metrics::Dashboard::UpdateDashboardService).to receive(:new).and_return(double(execute: { status: :error, message: 'something went wrong', http_status: :bad_request }))
|
||||
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status :bad_request
|
||||
expect(json_response).to eq('error' => 'something went wrong')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'missing branch' do
|
||||
let(:branch_name) { nil }
|
||||
|
||||
it 'raises responds with :bad_request status code and error message' do
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status :bad_request
|
||||
expect(json_response).to eq('error' => "Request parameter branch is missing.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without rights to push to repository' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it 'responds with :forbidden status code' do
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status :forbidden
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'project without repository feature' do
|
||||
let!(:project) { create(:project, name: 'dashboard-project', namespace: namespace) }
|
||||
|
||||
it 'responds with :not_found status code' do
|
||||
put :update, params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ describe 'Thread Comments Commit', :js do
|
|||
expect(page).to have_css('.js-note-emoji')
|
||||
end
|
||||
|
||||
it 'adds award to the correct note' do
|
||||
it 'adds award to the correct note', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/207973' do
|
||||
find("#note_#{commit_discussion_note2.id} .js-note-emoji").click
|
||||
first('.emoji-menu .js-emoji-btn').click
|
||||
|
||||
|
|
|
@ -37,6 +37,16 @@ describe 'Projects tree', :js do
|
|||
expect(page).not_to have_selector('.flash-alert')
|
||||
end
|
||||
|
||||
it 'renders tree table with non-ASCII filenames without errors' do
|
||||
visit project_tree_path(project, File.join(test_sha, 'encoding'))
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.tree-item')
|
||||
expect(page).to have_content('Files, encoding and much more')
|
||||
expect(page).to have_content('テスト.txt')
|
||||
expect(page).not_to have_selector('.flash-alert')
|
||||
end
|
||||
|
||||
context 'gravatar disabled' do
|
||||
let(:gravatar_enabled) { false }
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import GkeZoneDropdown from '~/create_cluster/gke_cluster/components/gke_zone_dropdown.vue';
|
||||
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
|
||||
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
|
||||
import { createStore } from '~/create_cluster/gke_cluster/store';
|
||||
import {
|
||||
SET_PROJECT,
|
||||
SET_ZONES,
|
||||
SET_PROJECT_BILLING_STATUS,
|
||||
} from '~/create_cluster/gke_cluster/store/mutation_types';
|
||||
import { selectedZoneMock, selectedProjectMock, gapiZonesResponseMock } from '../mock_data';
|
||||
|
||||
const propsData = {
|
||||
fieldId: 'cluster_provider_gcp_attributes_gcp_zone',
|
||||
fieldName: 'cluster[provider_gcp_attributes][gcp_zone]',
|
||||
};
|
||||
|
||||
const LABELS = {
|
||||
LOADING: 'Fetching zones',
|
||||
DISABLED: 'Select project to choose zone',
|
||||
DEFAULT: 'Select zone',
|
||||
};
|
||||
|
||||
describe('GkeZoneDropdown', () => {
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
wrapper = shallowMount(GkeZoneDropdown, { propsData, store });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('toggleText', () => {
|
||||
let dropdownButton;
|
||||
|
||||
beforeEach(() => {
|
||||
dropdownButton = wrapper.find(DropdownButton);
|
||||
});
|
||||
|
||||
it('returns disabled state toggle text', () => {
|
||||
expect(dropdownButton.props('toggleText')).toBe(LABELS.DISABLED);
|
||||
});
|
||||
|
||||
describe('isLoading', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setData({ isLoading: true });
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('returns loading toggle text', () => {
|
||||
expect(dropdownButton.props('toggleText')).toBe(LABELS.LOADING);
|
||||
});
|
||||
});
|
||||
|
||||
describe('project is set', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.$store.commit(SET_PROJECT, selectedProjectMock);
|
||||
wrapper.vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('returns default toggle text', () => {
|
||||
expect(dropdownButton.props('toggleText')).toBe(LABELS.DEFAULT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('project is selected', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.setItem(selectedZoneMock);
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('returns project name if project selected', () => {
|
||||
expect(dropdownButton.props('toggleText')).toBe(selectedZoneMock);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectItem', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items);
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('reflects new value when dropdown item is clicked', () => {
|
||||
const dropdown = wrapper.find(DropdownHiddenInput);
|
||||
|
||||
expect(dropdown.attributes('value')).toBe('');
|
||||
|
||||
wrapper.find('.dropdown-content button').trigger('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(dropdown.attributes('value')).toBe(selectedZoneMock);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -41,7 +41,7 @@ describe('Repository breadcrumbs component', () => {
|
|||
.findAll(RouterLinkStub)
|
||||
.at(3)
|
||||
.props('to'),
|
||||
).toEqual('/-/tree//app/assets/javascripts%23');
|
||||
).toEqual('/-/tree/app/assets/javascripts%23');
|
||||
});
|
||||
|
||||
it('renders last link as active', () => {
|
||||
|
|
|
@ -109,6 +109,26 @@ describe('Repository table row component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it.each`
|
||||
path
|
||||
${'test#'}
|
||||
${'Änderungen'}
|
||||
`('renders link for $path', ({ path }) => {
|
||||
factory({
|
||||
id: '1',
|
||||
sha: '123',
|
||||
path,
|
||||
type: 'tree',
|
||||
currentPath: '/',
|
||||
});
|
||||
|
||||
return vm.vm.$nextTick().then(() => {
|
||||
expect(vm.find({ ref: 'link' }).props('to')).toEqual({
|
||||
path: `/-/tree/master/${encodeURIComponent(path)}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('pushes new route for directory with hash', () => {
|
||||
factory({
|
||||
id: '1',
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
rules:
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/33025
|
||||
promise/no-nesting: off
|
|
@ -1,94 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import GkeZoneDropdown from '~/create_cluster/gke_cluster/components/gke_zone_dropdown.vue';
|
||||
import { createStore } from '~/create_cluster/gke_cluster/store';
|
||||
import {
|
||||
SET_PROJECT,
|
||||
SET_ZONES,
|
||||
SET_PROJECT_BILLING_STATUS,
|
||||
} from '~/create_cluster/gke_cluster/store/mutation_types';
|
||||
import { selectedZoneMock, selectedProjectMock, gapiZonesResponseMock } from '../mock_data';
|
||||
|
||||
const componentConfig = {
|
||||
fieldId: 'cluster_provider_gcp_attributes_gcp_zone',
|
||||
fieldName: 'cluster[provider_gcp_attributes][gcp_zone]',
|
||||
};
|
||||
|
||||
const LABELS = {
|
||||
LOADING: 'Fetching zones',
|
||||
DISABLED: 'Select project to choose zone',
|
||||
DEFAULT: 'Select zone',
|
||||
};
|
||||
|
||||
const createComponent = (store, props = componentConfig) => {
|
||||
const Component = Vue.extend(GkeZoneDropdown);
|
||||
|
||||
return mountComponentWithStore(Component, {
|
||||
el: null,
|
||||
props,
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
||||
describe('GkeZoneDropdown', () => {
|
||||
let vm;
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
vm = createComponent(store);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
describe('toggleText', () => {
|
||||
it('returns disabled state toggle text', () => {
|
||||
expect(vm.toggleText).toBe(LABELS.DISABLED);
|
||||
});
|
||||
|
||||
it('returns loading toggle text', () => {
|
||||
vm.isLoading = true;
|
||||
|
||||
expect(vm.toggleText).toBe(LABELS.LOADING);
|
||||
});
|
||||
|
||||
it('returns default toggle text', () => {
|
||||
expect(vm.toggleText).toBe(LABELS.DISABLED);
|
||||
|
||||
vm.$store.commit(SET_PROJECT, selectedProjectMock);
|
||||
vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
|
||||
|
||||
expect(vm.toggleText).toBe(LABELS.DEFAULT);
|
||||
});
|
||||
|
||||
it('returns project name if project selected', () => {
|
||||
vm.setItem(selectedZoneMock);
|
||||
|
||||
expect(vm.toggleText).toBe(selectedZoneMock);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectItem', () => {
|
||||
it('reflects new value when dropdown item is clicked', done => {
|
||||
expect(vm.$el.querySelector('input').value).toBe('');
|
||||
vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items);
|
||||
|
||||
return vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
vm.$el.querySelector('.dropdown-content button').click();
|
||||
|
||||
return vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock);
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
export const emptyProjectMock = {
|
||||
projectId: '',
|
||||
name: '',
|
||||
};
|
||||
|
||||
export const selectedProjectMock = {
|
||||
projectId: 'gcp-project-123',
|
||||
name: 'gcp-project',
|
||||
};
|
||||
|
||||
export const selectedZoneMock = 'us-central1-a';
|
||||
|
||||
export const selectedMachineTypeMock = 'n1-standard-2';
|
||||
|
||||
export const gapiProjectsResponseMock = {
|
||||
projects: [
|
||||
{
|
||||
projectNumber: '1234',
|
||||
projectId: 'gcp-project-123',
|
||||
lifecycleState: 'ACTIVE',
|
||||
name: 'gcp-project',
|
||||
createTime: '2017-12-16T01:48:29.129Z',
|
||||
parent: {
|
||||
type: 'organization',
|
||||
id: '12345',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const gapiZonesResponseMock = {
|
||||
kind: 'compute#zoneList',
|
||||
id: 'projects/gitlab-internal-153318/zones',
|
||||
items: [
|
||||
{
|
||||
kind: 'compute#zone',
|
||||
id: '2000',
|
||||
creationTimestamp: '1969-12-31T16:00:00.000-08:00',
|
||||
name: 'us-central1-a',
|
||||
description: 'us-central1-a',
|
||||
status: 'UP',
|
||||
region:
|
||||
'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/regions/us-central1',
|
||||
selfLink:
|
||||
'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a',
|
||||
availableCpuPlatforms: ['Intel Skylake', 'Intel Broadwell', 'Intel Sandy Bridge'],
|
||||
},
|
||||
],
|
||||
selfLink: 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones',
|
||||
};
|
||||
|
||||
export const gapiMachineTypesResponseMock = {
|
||||
kind: 'compute#machineTypeList',
|
||||
id: 'projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
|
||||
items: [
|
||||
{
|
||||
kind: 'compute#machineType',
|
||||
id: '3002',
|
||||
creationTimestamp: '1969-12-31T16:00:00.000-08:00',
|
||||
name: 'n1-standard-2',
|
||||
description: '2 vCPUs, 7.5 GB RAM',
|
||||
guestCpus: 2,
|
||||
memoryMb: 7680,
|
||||
imageSpaceGb: 10,
|
||||
maximumPersistentDisks: 64,
|
||||
maximumPersistentDisksSizeGb: '65536',
|
||||
zone: 'us-central1-a',
|
||||
selfLink:
|
||||
'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes/n1-standard-2',
|
||||
isSharedCpu: false,
|
||||
},
|
||||
],
|
||||
selfLink:
|
||||
'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
|
||||
};
|
|
@ -41,7 +41,9 @@ describe('diffs/components/commit_item', () => {
|
|||
expect(titleElement).toHaveText(commit.title_html);
|
||||
});
|
||||
|
||||
it('renders commit description', () => {
|
||||
// https://gitlab.com/gitlab-org/gitlab/issues/197139
|
||||
// eslint-disable-next-line jasmine/no-disabled-tests
|
||||
xit('renders commit description', () => {
|
||||
const descElement = getDescElement(vm);
|
||||
const descExpandElement = getDescExpandElement(vm);
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ export const issuable1 = {
|
|||
path: '/foo/bar/issues/123',
|
||||
state: 'opened',
|
||||
linkType: 'relates_to',
|
||||
dueDate: '2010-11-22',
|
||||
weight: 5,
|
||||
};
|
||||
|
||||
export const issuable2 = {
|
||||
|
|
|
@ -212,7 +212,7 @@ describe Gitlab::Profiler do
|
|||
stub_const('STDOUT', stdout)
|
||||
end
|
||||
|
||||
it 'prints a profile result sorted by total time' do
|
||||
it 'prints a profile result sorted by total time', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/206907' do
|
||||
described_class.print_by_total_time(result)
|
||||
|
||||
total_times =
|
||||
|
|
|
@ -12,7 +12,7 @@ describe Gitlab::ReactiveCacheSetCache, :clean_gitlab_redis_cache do
|
|||
subject { cache.cache_key(cache_prefix) }
|
||||
|
||||
it 'includes the suffix' do
|
||||
expect(subject).to eq "#{cache_prefix}:set"
|
||||
expect(subject).to eq "#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:#{cache_prefix}:set"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -129,6 +129,17 @@ describe Gitlab::TreeSummary do
|
|||
expect(commits).to satisfy_one { |c| c.id == whitespace_commit_sha }
|
||||
end
|
||||
end
|
||||
|
||||
context 'in a subdirectory with non-ASCII filenames' do
|
||||
let(:path) { 'encoding' }
|
||||
|
||||
it 'returns commits for entries in the subdirectory' do
|
||||
entry = entries.find { |x| x[:file_name] == 'テスト.txt' }
|
||||
|
||||
expect(entry).to be_a(Hash)
|
||||
expect(entry).to include(:commit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#more?' do
|
||||
|
|
|
@ -145,4 +145,28 @@ describe Ci::Processable do
|
|||
expect(another_build.reload.scheduling_type).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#needs_attributes' do
|
||||
let(:build) { create(:ci_build, :created, project: project, pipeline: pipeline) }
|
||||
|
||||
context 'with needs' do
|
||||
before do
|
||||
create(:ci_build_need, build: build, name: 'test1')
|
||||
create(:ci_build_need, build: build, name: 'test2')
|
||||
end
|
||||
|
||||
it 'returns all needs attributes' do
|
||||
expect(build.needs_attributes).to contain_exactly(
|
||||
{ 'artifacts' => true, 'name' => 'test1' },
|
||||
{ 'artifacts' => true, 'name' => 'test2' }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without needs' do
|
||||
it 'returns all needs attributes' do
|
||||
expect(build.needs_attributes).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -112,6 +112,43 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#with_reactive_cache_set', :use_clean_rails_redis_caching do
|
||||
subject(:go!) do
|
||||
instance.with_reactive_cache_set('resource', {}) do |data|
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
it 'calls with_reactive_cache' do
|
||||
expect(instance)
|
||||
.to receive(:with_reactive_cache)
|
||||
|
||||
go!
|
||||
end
|
||||
|
||||
context 'data returned' do
|
||||
let(:resource) { 'resource' }
|
||||
let(:set_key) { "#{cache_key}:#{resource}" }
|
||||
let(:set_cache) { Gitlab::ReactiveCacheSetCache.new }
|
||||
|
||||
before do
|
||||
stub_reactive_cache(instance, true, resource, {})
|
||||
end
|
||||
|
||||
it 'saves keys in set' do
|
||||
expect(set_cache.read(set_key)).to be_empty
|
||||
|
||||
go!
|
||||
|
||||
expect(set_cache.read(set_key)).not_to be_empty
|
||||
end
|
||||
|
||||
it 'returns the data' do
|
||||
expect(go!).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.reactive_cache_worker_finder' do
|
||||
context 'with default reactive_cache_worker_finder' do
|
||||
let(:args) { %w(other args) }
|
||||
|
|
|
@ -8,7 +8,7 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
subject { create(:project_error_tracking_setting, project: project) }
|
||||
subject(:setting) { create(:project_error_tracking_setting, project: project) }
|
||||
|
||||
describe 'Associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
|
@ -453,4 +453,23 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expire_issues_cache', :use_clean_rails_redis_caching do
|
||||
let(:issues) { [:some, :issues] }
|
||||
let(:opt) { 'list_issues' }
|
||||
let(:params) { { issue_status: 'unresolved', limit: 20, sort: 'last_seen' } }
|
||||
|
||||
before do
|
||||
start_reactive_cache_lifetime(subject, opt, params.stringify_keys)
|
||||
stub_reactive_cache(subject, issues, opt, params.stringify_keys)
|
||||
end
|
||||
|
||||
it 'clears the cache' do
|
||||
expect(subject.list_sentry_issues(params)).to eq(issues)
|
||||
|
||||
subject.expire_issues_cache
|
||||
|
||||
expect(subject.list_sentry_issues(params)).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ describe API::Markdown do
|
|||
|
||||
shared_examples "rendered markdown text without GFM" do
|
||||
it "renders markdown text" do
|
||||
expect(response).to have_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["html"]).to eq("<p>#{text}</p>")
|
||||
|
@ -25,7 +25,7 @@ describe API::Markdown do
|
|||
|
||||
shared_examples "404 Project Not Found" do
|
||||
it "responses with 404 Not Found" do
|
||||
expect(response).to have_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["message"]).to eq("404 Project Not Found")
|
||||
|
@ -37,7 +37,7 @@ describe API::Markdown do
|
|||
let(:params) { {} }
|
||||
|
||||
it "responses with 400 Bad Request" do
|
||||
expect(response).to have_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["error"]).to eq("text is missing")
|
||||
|
@ -83,7 +83,7 @@ describe API::Markdown do
|
|||
let(:params) { { text: text, gfm: true } }
|
||||
|
||||
it "renders markdown text" do
|
||||
expect(response).to have_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["html"]).to include("Hello world!")
|
||||
|
@ -100,7 +100,7 @@ describe API::Markdown do
|
|||
let(:user) { project.owner }
|
||||
|
||||
it "renders markdown text" do
|
||||
expect(response).to have_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["html"]).to include("Hello world!")
|
||||
|
@ -120,7 +120,7 @@ describe API::Markdown do
|
|||
|
||||
shared_examples 'user without proper access' do
|
||||
it 'does not render the title or link' do
|
||||
expect(response).to have_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response["html"]).not_to include('Confidential title')
|
||||
expect(json_response["html"]).not_to include('<a href=')
|
||||
expect(json_response["html"]).to include('Hello world!')
|
||||
|
@ -146,7 +146,7 @@ describe API::Markdown do
|
|||
let(:user) { confidential_issue.author }
|
||||
|
||||
it 'renders the title or link' do
|
||||
expect(response).to have_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response["html"]).to include('Confidential title')
|
||||
expect(json_response["html"]).to include('Hello world!')
|
||||
.and include('data-name="tada"')
|
||||
|
|
|
@ -43,7 +43,7 @@ describe API::Members do
|
|||
|
||||
get api(members_url, user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.size).to eq(2)
|
||||
|
@ -72,7 +72,7 @@ describe API::Members do
|
|||
|
||||
get api(members_url, developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.size).to eq(2)
|
||||
|
@ -82,7 +82,7 @@ describe API::Members do
|
|||
it 'finds members with query string' do
|
||||
get api(members_url, developer), params: { query: maintainer.username }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.count).to eq(1)
|
||||
|
@ -92,7 +92,7 @@ describe API::Members do
|
|||
it 'finds members with the given user_ids' do
|
||||
get api(members_url, developer), params: { user_ids: [maintainer.id, developer.id, stranger.id] }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.map { |u| u['id'] }).to contain_exactly(maintainer.id, developer.id)
|
||||
|
@ -101,7 +101,7 @@ describe API::Members do
|
|||
it 'finds all members with no query specified' do
|
||||
get api(members_url, developer), params: { query: '' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.count).to eq(2)
|
||||
|
@ -137,7 +137,7 @@ describe API::Members do
|
|||
it 'finds all project members including inherited members' do
|
||||
get api("/projects/#{project.id}/members/all", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id, project_user.id, linked_group_user.id]
|
||||
|
@ -148,7 +148,7 @@ describe API::Members do
|
|||
|
||||
get api("/projects/#{project.id}/members/all", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
|
||||
|
@ -165,7 +165,7 @@ describe API::Members do
|
|||
it 'finds all group members including inherited members' do
|
||||
get api("/groups/#{nested_group.id}/members/all", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id]
|
||||
|
@ -185,7 +185,7 @@ describe API::Members do
|
|||
user = public_send(type)
|
||||
get api("/#{source_type.pluralize}/#{source.id}/members/#{all ? 'all/' : ''}#{developer.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
# User attributes
|
||||
expect(json_response['id']).to eq(developer.id)
|
||||
expect(json_response['name']).to eq(developer.name)
|
||||
|
@ -220,7 +220,7 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", user),
|
||||
params: { user_id: access_requester.id, access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -233,7 +233,7 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: access_requester.id, access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end.to change { source.members.count }.by(1)
|
||||
expect(source.requesters.count).to eq(0)
|
||||
expect(json_response['id']).to eq(access_requester.id)
|
||||
|
@ -246,7 +246,7 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end.to change { source.members.count }.by(1)
|
||||
expect(json_response['id']).to eq(stranger.id)
|
||||
expect(json_response['access_level']).to eq(Member::DEVELOPER)
|
||||
|
@ -265,7 +265,7 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: stranger.id, access_level: Member::REPORTER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']['access_level']).to eq(["should be greater than or equal to Developer inherited membership from group #{parent.name}"])
|
||||
end
|
||||
|
||||
|
@ -279,7 +279,7 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: stranger.id, access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['id']).to eq(stranger.id)
|
||||
expect(json_response['access_level']).to eq(Member::MAINTAINER)
|
||||
end
|
||||
|
@ -289,14 +289,14 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: maintainer.id, access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(409)
|
||||
expect(response).to have_gitlab_http_status(:conflict)
|
||||
end
|
||||
|
||||
it 'returns 404 when the user_id is not valid' do
|
||||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: 0, access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('404 User Not Found')
|
||||
end
|
||||
|
||||
|
@ -304,21 +304,21 @@ describe API::Members do
|
|||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
|
||||
it 'returns 400 when access_level is not given' do
|
||||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: stranger.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
|
||||
it 'returns 400 when access_level is not valid' do
|
||||
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
|
||||
params: { user_id: stranger.id, access_level: 1234 }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -340,7 +340,7 @@ describe API::Members do
|
|||
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user),
|
||||
params: { access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -351,7 +351,7 @@ describe API::Members do
|
|||
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
|
||||
params: { access_level: Member::MAINTAINER, expires_at: '2016-08-05' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['id']).to eq(developer.id)
|
||||
expect(json_response['access_level']).to eq(Member::MAINTAINER)
|
||||
expect(json_response['expires_at']).to eq('2016-08-05')
|
||||
|
@ -362,20 +362,20 @@ describe API::Members do
|
|||
put api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer),
|
||||
params: { access_level: Member::MAINTAINER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns 400 when access_level is not given' do
|
||||
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
|
||||
it 'returns 400 when access level is not valid' do
|
||||
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
|
||||
params: { access_level: 1234 }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -393,7 +393,7 @@ describe API::Members do
|
|||
user = public_send(type)
|
||||
delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -404,7 +404,7 @@ describe API::Members do
|
|||
expect do
|
||||
delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(204)
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
end.to change { source.members.count }.by(-1)
|
||||
end
|
||||
end
|
||||
|
@ -415,7 +415,7 @@ describe API::Members do
|
|||
expect do
|
||||
delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", maintainer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end.not_to change { source.requesters.count }
|
||||
end
|
||||
end
|
||||
|
@ -424,7 +424,7 @@ describe API::Members do
|
|||
expect do
|
||||
delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(204)
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
end.to change { source.members.count }.by(-1)
|
||||
end
|
||||
|
||||
|
@ -436,7 +436,7 @@ describe API::Members do
|
|||
it 'returns 404 if member does not exist' do
|
||||
delete api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -491,7 +491,7 @@ describe API::Members do
|
|||
post api("/projects/#{project.id}/members", maintainer),
|
||||
params: { user_id: stranger.id, access_level: Member::OWNER }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end.to change { project.members.count }.by(0)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,12 +28,12 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
|
|||
|
||||
it 'returns a 404 when merge_request id is used instead of the iid' do
|
||||
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns a 404 when merge_request_iid not found' do
|
||||
get api("/projects/#{project.id}/merge_requests/0/versions", user)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,17 +51,17 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
|
|||
|
||||
it 'returns a 404 when merge_request id is used instead of the iid' do
|
||||
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns a 404 when merge_request version_id is not found' do
|
||||
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/0", user)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns a 404 when merge_request_iid is not found' do
|
||||
get api("/projects/#{project.id}/merge_requests/12345/versions/#{merge_request_diff.id}", user)
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,7 +12,7 @@ describe API::Namespaces do
|
|||
context "when unauthenticated" do
|
||||
it "returns authentication error" do
|
||||
get api("/namespaces")
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,7 +23,7 @@ describe API::Namespaces do
|
|||
group_kind_json_response = json_response.find { |resource| resource['kind'] == 'group' }
|
||||
user_kind_json_response = json_response.find { |resource| resource['kind'] == 'user' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(group_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
|
||||
'parent_id', 'members_count_with_descendants')
|
||||
|
@ -34,7 +34,7 @@ describe API::Namespaces do
|
|||
it "admin: returns an array of all namespaces" do
|
||||
get api("/namespaces", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.length).to eq(Namespace.count)
|
||||
|
@ -43,7 +43,7 @@ describe API::Namespaces do
|
|||
it "admin: returns an array of matched namespaces" do
|
||||
get api("/namespaces?search=#{group2.name}", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.length).to eq(1)
|
||||
|
@ -77,7 +77,7 @@ describe API::Namespaces do
|
|||
it "user: returns an array of namespaces" do
|
||||
get api("/namespaces", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.length).to eq(1)
|
||||
|
@ -86,7 +86,7 @@ describe API::Namespaces do
|
|||
it "admin: returns an array of matched namespaces" do
|
||||
get api("/namespaces?search=#{user.username}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.length).to eq(1)
|
||||
|
@ -102,7 +102,7 @@ describe API::Namespaces do
|
|||
it 'returns namespace details' do
|
||||
get api("/namespaces/#{namespace_id}", request_actor)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
||||
expect(json_response['id']).to eq(requested_namespace.id)
|
||||
expect(json_response['path']).to eq(requested_namespace.path)
|
||||
|
@ -153,7 +153,7 @@ describe API::Namespaces do
|
|||
it 'returns not-found' do
|
||||
get api('/namespaces/0', request_actor)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -162,7 +162,7 @@ describe API::Namespaces do
|
|||
it 'returns authentication error' do
|
||||
get api("/namespaces/#{group1.id}")
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -174,7 +174,7 @@ describe API::Namespaces do
|
|||
it 'returns not-found' do
|
||||
get api("/namespaces/#{group2.id}", request_actor)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -182,7 +182,7 @@ describe API::Namespaces do
|
|||
it 'returns not-found' do
|
||||
get api("/namespaces/#{user2.namespace.id}", request_actor)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,7 +72,7 @@ describe API::Notes do
|
|||
it "returns an empty array" do
|
||||
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response).to be_empty
|
||||
|
@ -86,7 +86,7 @@ describe API::Notes do
|
|||
it "returns 404" do
|
||||
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -95,7 +95,7 @@ describe API::Notes do
|
|||
it "returns a non-empty array" do
|
||||
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", private_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(json_response.first['body']).to eq(cross_reference_note.note)
|
||||
|
@ -114,7 +114,7 @@ describe API::Notes do
|
|||
|
||||
shared_examples 'a notes request' do
|
||||
it 'is a note array response' do
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
end
|
||||
|
@ -177,7 +177,7 @@ describe API::Notes do
|
|||
it "returns a 404 error" do
|
||||
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context "when issue is confidential" do
|
||||
|
@ -188,7 +188,7 @@ describe API::Notes do
|
|||
it "returns 404" do
|
||||
get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -197,7 +197,7 @@ describe API::Notes do
|
|||
it "returns an issue note by id" do
|
||||
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", private_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['body']).to eq(cross_reference_note.note)
|
||||
end
|
||||
end
|
||||
|
@ -237,7 +237,7 @@ describe API::Notes do
|
|||
it 'returns 200 status' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end
|
||||
|
||||
it 'creates a new note' do
|
||||
|
@ -251,7 +251,7 @@ describe API::Notes do
|
|||
it 'returns 403 status' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'does not create a new note' do
|
||||
|
|
|
@ -11,7 +11,7 @@ describe API::NotificationSettings do
|
|||
it "returns global notification settings for the current user" do
|
||||
get api("/notification_settings", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_a Hash
|
||||
expect(json_response['notification_email']).to eq(user.notification_email)
|
||||
expect(json_response['level']).to eq(user.global_notification_setting.level)
|
||||
|
@ -24,7 +24,7 @@ describe API::NotificationSettings do
|
|||
it "updates global notification settings for the current user" do
|
||||
put api("/notification_settings", user), params: { level: 'watch', notification_email: email.email }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['notification_email']).to eq(email.email)
|
||||
expect(user.reload.notification_email).to eq(email.email)
|
||||
expect(json_response['level']).to eq(user.reload.global_notification_setting.level)
|
||||
|
@ -35,7 +35,7 @@ describe API::NotificationSettings do
|
|||
it "fails on non-user email address" do
|
||||
put api("/notification_settings", user), params: { notification_email: 'invalid@example.com' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,7 +43,7 @@ describe API::NotificationSettings do
|
|||
it "returns group level notification settings for the current user" do
|
||||
get api("/groups/#{group.id}/notification_settings", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_a Hash
|
||||
expect(json_response['level']).to eq(user.notification_settings_for(group).level)
|
||||
end
|
||||
|
@ -53,7 +53,7 @@ describe API::NotificationSettings do
|
|||
it "updates group level notification settings for the current user" do
|
||||
put api("/groups/#{group.id}/notification_settings", user), params: { level: 'watch' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['level']).to eq(user.reload.notification_settings_for(group).level)
|
||||
end
|
||||
end
|
||||
|
@ -62,7 +62,7 @@ describe API::NotificationSettings do
|
|||
it "returns project level notification settings for the current user" do
|
||||
get api("/projects/#{project.id}/notification_settings", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_a Hash
|
||||
expect(json_response['level']).to eq(user.notification_settings_for(project).level)
|
||||
end
|
||||
|
@ -72,7 +72,7 @@ describe API::NotificationSettings do
|
|||
it "updates project level notification settings for the current user" do
|
||||
put api("/projects/#{project.id}/notification_settings", user), params: { level: 'custom', new_note: true }
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['level']).to eq(user.reload.notification_settings_for(project).level)
|
||||
expect(json_response['events']['new_note']).to be_truthy
|
||||
expect(json_response['events']['new_issue']).to be_falsey
|
||||
|
@ -83,7 +83,7 @@ describe API::NotificationSettings do
|
|||
it "fails on invalid level" do
|
||||
put api("/projects/#{project.id}/notification_settings", user), params: { level: 'invalid' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ describe 'OAuth tokens' do
|
|||
|
||||
request_oauth_token(user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(401)
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
expect(json_response['error']).to eq('invalid_grant')
|
||||
end
|
||||
end
|
||||
|
@ -25,7 +25,7 @@ describe 'OAuth tokens' do
|
|||
|
||||
request_oauth_token(user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['access_token']).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
@ -33,7 +33,7 @@ describe 'OAuth tokens' do
|
|||
shared_examples 'does not create an access token' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it { expect(response).to have_gitlab_http_status(401) }
|
||||
it { expect(response).to have_gitlab_http_status(:unauthorized) }
|
||||
end
|
||||
|
||||
context 'when user is blocked' do
|
||||
|
|
|
@ -1979,6 +1979,21 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when object storage throws errors' do
|
||||
let(:params) { { artifact_type: :archive, artifact_format: :zip } }
|
||||
|
||||
it 'does not store artifacts' do
|
||||
allow_next_instance_of(JobArtifactUploader) do |uploader|
|
||||
allow(uploader).to receive(:store!).and_raise(Errno::EIO)
|
||||
end
|
||||
|
||||
upload_artifacts(file_upload, headers_with_token, params)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
expect(job.reload.job_artifacts_archive).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when artifacts are being stored outside of tmp path' do
|
||||
let(:new_tmpdir) { Dir.mktmpdir }
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::CreateJobArtifactsService do
|
||||
let(:service) { described_class.new }
|
||||
let(:job) { create(:ci_build) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let(:service) { described_class.new(project) }
|
||||
let(:job) { create(:ci_build, project: project) }
|
||||
let(:artifacts_sha256) { '0' * 64 }
|
||||
let(:metadata_file) { nil }
|
||||
|
||||
|
@ -64,7 +65,7 @@ describe Ci::CreateJobArtifactsService do
|
|||
it 'sets expiration date according to application settings' do
|
||||
expected_expire_at = 1.day.from_now
|
||||
|
||||
expect(subject).to be_truthy
|
||||
expect(subject).to match(a_hash_including(status: :success))
|
||||
archive_artifact, metadata_artifact = job.job_artifacts.last(2)
|
||||
|
||||
expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at)
|
||||
|
@ -80,7 +81,7 @@ describe Ci::CreateJobArtifactsService do
|
|||
it 'sets expiration date according to the parameter' do
|
||||
expected_expire_at = 2.hours.from_now
|
||||
|
||||
expect(subject).to be_truthy
|
||||
expect(subject).to match(a_hash_including(status: :success))
|
||||
archive_artifact, metadata_artifact = job.job_artifacts.last(2)
|
||||
|
||||
expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at)
|
||||
|
@ -101,21 +102,50 @@ describe Ci::CreateJobArtifactsService do
|
|||
|
||||
it 'ignores the changes' do
|
||||
expect { subject }.not_to change { Ci::JobArtifact.count }
|
||||
expect(subject).to be_truthy
|
||||
expect(subject).to match(a_hash_including(status: :success))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sha256 of uploading artifact is different than the existing one' do
|
||||
let(:existing_sha256) { '1' * 64 }
|
||||
|
||||
it 'returns false and logs the error' do
|
||||
it 'returns error status' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
|
||||
|
||||
expect { subject }.not_to change { Ci::JobArtifact.count }
|
||||
expect(subject).to be_falsey
|
||||
expect(job.errors[:base]).to contain_exactly('another artifact of the same type already exists')
|
||||
expect(subject).to match(
|
||||
a_hash_including(http_status: :bad_request,
|
||||
message: 'another artifact of the same type already exists',
|
||||
status: :error))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'rescues object storage error' do |klass, message, expected_message|
|
||||
it "handles #{klass}" do
|
||||
allow_next_instance_of(JobArtifactUploader) do |uploader|
|
||||
allow(uploader).to receive(:store!).and_raise(klass, message)
|
||||
end
|
||||
|
||||
expect(Gitlab::ErrorTracking)
|
||||
.to receive(:track_exception)
|
||||
.and_call_original
|
||||
|
||||
expect(subject).to match(
|
||||
a_hash_including(
|
||||
http_status: :service_unavailable,
|
||||
message: expected_message || message,
|
||||
status: :error))
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'rescues object storage error',
|
||||
Errno::EIO, 'some/path', 'Input/output error - some/path'
|
||||
|
||||
it_behaves_like 'rescues object storage error',
|
||||
Google::Apis::ServerError, 'Server error'
|
||||
|
||||
it_behaves_like 'rescues object storage error',
|
||||
Signet::RemoteServerError, 'The service is currently unavailable'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,7 +36,7 @@ describe Ci::RetryBuildService do
|
|||
job_artifacts_performance job_artifacts_lsif
|
||||
job_artifacts_codequality job_artifacts_metrics scheduled_at
|
||||
job_variables waiting_for_resource_at job_artifacts_metrics_referee
|
||||
job_artifacts_network_referee].freeze
|
||||
job_artifacts_network_referee needs].freeze
|
||||
|
||||
IGNORE_ACCESSORS =
|
||||
%i[type lock_version target_url base_tags trace_sections
|
||||
|
@ -46,7 +46,8 @@ describe Ci::RetryBuildService do
|
|||
sourced_pipelines artifacts_file_store artifacts_metadata_store
|
||||
metadata runner_session trace_chunks upstream_pipeline_id
|
||||
artifacts_file artifacts_metadata artifacts_size commands
|
||||
resource resource_group_id processed security_scans].freeze
|
||||
resource resource_group_id processed security_scans author
|
||||
pipeline_id].freeze
|
||||
|
||||
shared_examples 'build duplication' do
|
||||
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
|
||||
|
@ -79,8 +80,15 @@ describe Ci::RetryBuildService do
|
|||
end
|
||||
|
||||
describe 'clone accessors' do
|
||||
let(:forbidden_associations) do
|
||||
Ci::Build.reflect_on_all_associations.each_with_object(Set.new) do |assoc, memo|
|
||||
memo << assoc.name unless assoc.macro == :belongs_to
|
||||
end
|
||||
end
|
||||
|
||||
CLONE_ACCESSORS.each do |attribute|
|
||||
it "clones #{attribute} build attribute" do
|
||||
expect(attribute).not_to be_in(forbidden_associations), "association #{attribute} must be `belongs_to`"
|
||||
expect(build.send(attribute)).not_to be_nil
|
||||
expect(new_build.send(attribute)).not_to be_nil
|
||||
expect(new_build.send(attribute)).to eq build.send(attribute)
|
||||
|
@ -97,9 +105,17 @@ describe Ci::RetryBuildService do
|
|||
expect(new_build.protected).to eq build.protected
|
||||
end
|
||||
end
|
||||
|
||||
it 'clones only the needs attributes' do
|
||||
expect(new_build.needs.exists?).to be_truthy
|
||||
expect(build.needs.exists?).to be_truthy
|
||||
|
||||
expect(new_build.needs_attributes).to match(build.needs_attributes)
|
||||
expect(new_build.needs).not_to match(build.needs)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'reject acessors' do
|
||||
describe 'reject accessors' do
|
||||
REJECT_ACCESSORS.each do |attribute|
|
||||
it "does not clone #{attribute} build attribute" do
|
||||
expect(new_build.send(attribute)).not_to eq build.send(attribute)
|
||||
|
@ -117,8 +133,9 @@ describe Ci::RetryBuildService do
|
|||
#
|
||||
current_accessors =
|
||||
Ci::Build.attribute_names.map(&:to_sym) +
|
||||
Ci::Build.attribute_aliases.keys.map(&:to_sym) +
|
||||
Ci::Build.reflect_on_all_associations.map(&:name) +
|
||||
[:tag_list]
|
||||
[:tag_list, :needs_attributes]
|
||||
|
||||
current_accessors.uniq!
|
||||
|
||||
|
|
|
@ -43,6 +43,12 @@ describe ErrorTracking::IssueUpdateService do
|
|||
update_service.execute
|
||||
end
|
||||
|
||||
it 'clears the reactive cache' do
|
||||
expect(error_tracking_setting).to receive(:expire_issues_cache)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
context 'related issue and resolving' do
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let(:sentry_issue) { create(:sentry_issue, issue: issue) }
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_store_caching do
|
||||
include MetricsDashboardHelpers
|
||||
|
||||
set(:user) { create(:user) }
|
||||
set(:project) { create(:project, :repository) }
|
||||
set(:environment) { create(:environment, project: project) }
|
||||
|
||||
describe '#execute' do
|
||||
subject(:service_call) { described_class.new(project, user, params).execute }
|
||||
|
||||
let(:commit_message) { 'test' }
|
||||
let(:branch) { 'dashboard_new_branch' }
|
||||
let(:dashboard) { 'config/prometheus/common_metrics.yml' }
|
||||
let(:file_name) { 'custom_dashboard.yml' }
|
||||
let(:file_content_hash) { YAML.safe_load(File.read(dashboard)) }
|
||||
let(:params) do
|
||||
{
|
||||
file_name: file_name,
|
||||
file_content: file_content_hash,
|
||||
commit_message: commit_message,
|
||||
branch: branch
|
||||
}
|
||||
end
|
||||
|
||||
context 'user does not have push right to repository' do
|
||||
it_behaves_like 'misconfigured dashboard service response', :forbidden, "You can't commit to this project"
|
||||
end
|
||||
|
||||
context 'with rights to push to the repository' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'path traversal attack attempt' do
|
||||
context 'with a yml extension' do
|
||||
let(:file_name) { 'config/prometheus/../database.yml' }
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :bad_request, "A file with this name doesn't exist"
|
||||
end
|
||||
|
||||
context 'without a yml extension' do
|
||||
let(:file_name) { '../../..../etc/passwd' }
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :bad_request, "The file name should have a .yml extension"
|
||||
end
|
||||
end
|
||||
|
||||
context 'valid parameters' do
|
||||
it_behaves_like 'valid dashboard update process'
|
||||
end
|
||||
|
||||
context 'selected branch already exists' do
|
||||
let(:branch) { 'existing_branch' }
|
||||
|
||||
before do
|
||||
project.repository.add_branch(user, branch, 'master')
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :bad_request, "There was an error updating the dashboard, branch named: existing_branch already exists."
|
||||
end
|
||||
|
||||
context 'Files::UpdateService success' do
|
||||
before do
|
||||
allow(::Files::UpdateService).to receive(:new).and_return(double(execute: { status: :success }))
|
||||
end
|
||||
|
||||
it 'returns success', :aggregate_failures do
|
||||
dashboard_details = {
|
||||
path: '.gitlab/dashboards/custom_dashboard.yml',
|
||||
display_name: 'custom_dashboard.yml',
|
||||
default: false,
|
||||
system_dashboard: false
|
||||
}
|
||||
|
||||
expect(service_call[:status]).to be :success
|
||||
expect(service_call[:http_status]).to be :created
|
||||
expect(service_call[:dashboard]).to match dashboard_details
|
||||
end
|
||||
|
||||
context 'with escaped characters in file name' do
|
||||
let(:file_name) { "custom_dashboard%26copy.yml" }
|
||||
|
||||
it 'escapes the special characters', :aggregate_failures do
|
||||
dashboard_details = {
|
||||
path: '.gitlab/dashboards/custom_dashboard©.yml',
|
||||
display_name: 'custom_dashboard©.yml',
|
||||
default: false,
|
||||
system_dashboard: false
|
||||
}
|
||||
|
||||
expect(service_call[:status]).to be :success
|
||||
expect(service_call[:http_status]).to be :created
|
||||
expect(service_call[:dashboard]).to match dashboard_details
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Files::UpdateService fails' do
|
||||
before do
|
||||
allow(::Files::UpdateService).to receive(:new).and_return(double(execute: { status: :error }))
|
||||
end
|
||||
|
||||
it 'returns error' do
|
||||
expect(service_call[:status]).to be :error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -85,3 +85,24 @@ RSpec.shared_examples 'valid dashboard cloning process' do |dashboard_template,
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'valid dashboard update process' do
|
||||
let(:dashboard_attrs) do
|
||||
{
|
||||
commit_message: commit_message,
|
||||
branch_name: branch,
|
||||
start_branch: project.default_branch,
|
||||
encoding: 'text',
|
||||
file_path: ".gitlab/dashboards/#{file_name}",
|
||||
file_content: ::PerformanceMonitoring::PrometheusDashboard.from_json(file_content_hash).to_yaml
|
||||
}
|
||||
end
|
||||
|
||||
it 'delegates commit creation to Files::UpdateService', :aggregate_failures do
|
||||
service_instance = instance_double(::Files::UpdateService)
|
||||
expect(::Files::UpdateService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
|
||||
expect(service_instance).to receive(:execute).and_return(status: :success)
|
||||
|
||||
service_call
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue