Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b922b2f47a
commit
e5d314d432
|
|
@ -11,7 +11,7 @@
|
|||
if: '$CI_PROJECT_NAME != "gitlab-foss" && $CI_PROJECT_NAME != "gitlab-ce" && $CI_PROJECT_NAME != "gitlabhq"'
|
||||
|
||||
.if-default-refs: &if-default-refs
|
||||
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
|
||||
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
|
||||
|
||||
.if-master-refs: &if-master-refs
|
||||
if: '$CI_COMMIT_REF_NAME == "master"'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
export const DEFAULT_RX = 0.4;
|
||||
export const DEFAULT_BAR_WIDTH = 6;
|
||||
export const DEFAULT_LABEL_WIDTH = 4;
|
||||
export const DEFAULT_LABEL_HEIGHT = 5;
|
||||
export const BAR_HEIGHTS = [5, 7, 9, 14, 21, 35, 50, 80];
|
||||
export const GRID_YS = [30, 60, 90];
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<script>
|
||||
import { GlSkeletonLoader } from '@gitlab/ui';
|
||||
|
||||
import {
|
||||
DEFAULT_RX,
|
||||
DEFAULT_BAR_WIDTH,
|
||||
DEFAULT_LABEL_WIDTH,
|
||||
DEFAULT_LABEL_HEIGHT,
|
||||
BAR_HEIGHTS,
|
||||
GRID_YS,
|
||||
} from './constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlSkeletonLoader,
|
||||
},
|
||||
props: {
|
||||
barWidth: {
|
||||
type: Number,
|
||||
default: DEFAULT_BAR_WIDTH,
|
||||
required: false,
|
||||
},
|
||||
labelWidth: {
|
||||
type: Number,
|
||||
default: DEFAULT_LABEL_WIDTH,
|
||||
required: false,
|
||||
},
|
||||
labelHeight: {
|
||||
type: Number,
|
||||
default: DEFAULT_LABEL_HEIGHT,
|
||||
required: false,
|
||||
},
|
||||
rx: {
|
||||
type: Number,
|
||||
default: DEFAULT_RX,
|
||||
required: false,
|
||||
},
|
||||
// skeleton-loader will generate a unique key if not defined
|
||||
uniqueKey: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
labelCentering() {
|
||||
return (this.barWidth - this.labelWidth) / 2;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getBarXPosition(index) {
|
||||
const numberOfBars = this.$options.BAR_HEIGHTS.length;
|
||||
const numberOfSpaces = numberOfBars + 1;
|
||||
const spaceBetweenBars = (100 - numberOfSpaces * this.barWidth) / numberOfBars;
|
||||
|
||||
return (0.5 + index) * (this.barWidth + spaceBetweenBars);
|
||||
},
|
||||
},
|
||||
BAR_HEIGHTS,
|
||||
GRID_YS,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-skeleton-loader :unique-key="uniqueKey">
|
||||
<rect
|
||||
v-for="(y, index) in $options.GRID_YS"
|
||||
:key="`grid-${index}`"
|
||||
data-testid="skeleton-chart-grid"
|
||||
x="0"
|
||||
:y="`${y}%`"
|
||||
width="100%"
|
||||
height="1px"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`bar-${index}`"
|
||||
data-testid="skeleton-chart-bar"
|
||||
:x="`${getBarXPosition(index)}%`"
|
||||
:y="`${90 - height}%`"
|
||||
:width="`${barWidth}%`"
|
||||
:height="`${height}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`label-${index}`"
|
||||
data-testid="skeleton-chart-label"
|
||||
:x="`${labelCentering + getBarXPosition(index)}%`"
|
||||
:y="`${100 - labelHeight}%`"
|
||||
:width="`${labelWidth}%`"
|
||||
:height="`${labelHeight}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
</gl-skeleton-loader>
|
||||
</template>
|
||||
|
|
@ -151,6 +151,7 @@
|
|||
|
||||
.design-dropzone-card {
|
||||
transition: border $general-hover-transition-duration $general-hover-transition-curve;
|
||||
color: $gl-text-color;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ textarea {
|
|||
|
||||
input {
|
||||
border-radius: $border-radius-base;
|
||||
color: $gl-text-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
|
||||
input[type='text'].danger {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,13 @@
|
|||
|
||||
.timeline-entry {
|
||||
color: $gl-text-color;
|
||||
background-color: $white;
|
||||
|
||||
// [dark-theme]: only give background color to actual notes
|
||||
// in the timeline, the note form textarea has a background
|
||||
// of it's own
|
||||
&:not(.note-form) {
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.timeline-entry-inner {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -479,9 +479,9 @@ $gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
|
|||
$added: #63c363;
|
||||
$deleted: #f77;
|
||||
$line-added: #ecfdf0;
|
||||
$line-added-dark: #c7f0d2;
|
||||
$line-added-dark: #c7f0d2 !default;
|
||||
$line-removed: #fbe9eb;
|
||||
$line-removed-dark: #fac5cd;
|
||||
$line-removed-dark: #fac5cd !default;
|
||||
$line-number-old: #f9d7dc;
|
||||
$line-number-new: #ddfbe6;
|
||||
$line-number-select: #fbf2da;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ $btn-line-height: 20px;
|
|||
$table-accent-bg: $gray-light;
|
||||
$table-border-color: $gray-200;
|
||||
$card-border-color: $border-color;
|
||||
$card-cap-bg: $gray-light;
|
||||
$card-cap-bg: $gray-light !default;
|
||||
$success: $green-500;
|
||||
$info: $blue-500;
|
||||
$warning: $orange-500;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@
|
|||
background-color: $white;
|
||||
|
||||
&.is-focused {
|
||||
@extend .form-control:focus;
|
||||
border-color: $input-focus-border-color;
|
||||
box-shadow: $input-focus-box-shadow;
|
||||
|
||||
.comment-toolbar,
|
||||
.nav-links {
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ $input-focus-bg: $gray-100;
|
|||
$input-color: $gray-900;
|
||||
$input-group-addon-bg: $gray-900;
|
||||
|
||||
$card-cap-bg: $gray-50;
|
||||
|
||||
$tooltip-bg: $gray-800;
|
||||
$tooltip-color: $gray-10;
|
||||
|
||||
|
|
@ -118,6 +120,11 @@ $issues-today-border: #333a40;
|
|||
$yiq-text-dark: $gray-50;
|
||||
$yiq-text-light: $gray-950;
|
||||
|
||||
// Commit Diff Colors
|
||||
$line-added-dark: $green-200;
|
||||
$line-removed-dark: $red-200;
|
||||
|
||||
// Misc component overrides that should live elsewhere
|
||||
.gl-label {
|
||||
filter: brightness(0.9) contrast(1.1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
class Dashboard::ProjectsController < Dashboard::ApplicationController
|
||||
include ParamsBackwardCompatibility
|
||||
include RendersMemberAccess
|
||||
include OnboardingExperimentHelper
|
||||
include SortingHelper
|
||||
include SortingPreference
|
||||
include FiltersEvents
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ module Types
|
|||
value 'UPDATED_TIME_DESC', 'Created time by descending order', value: :updated_at_desc
|
||||
value 'EVENT_COUNT_ASC', 'Events count by ascending order', value: :event_count_asc
|
||||
value 'EVENT_COUNT_DESC', 'Events count by descending order', value: :event_count_desc
|
||||
value 'SEVERITY_ASC', 'Severity by ascending order', value: :severity_asc
|
||||
value 'SEVERITY_DESC', 'Severity by descending order', value: :severity_desc
|
||||
value 'STATUS_ASC', 'Status by ascending order', value: :status_asc
|
||||
value 'STATUS_DESC', 'Status by descending order', value: :status_desc
|
||||
value 'SEVERITY_ASC', 'Severity from less critical to more critical', value: :severity_asc
|
||||
value 'SEVERITY_DESC', 'Severity from more critical to less critical', value: :severity_desc
|
||||
value 'STATUS_ASC', 'Status by order: Ignored > Resolved > Acknowledged > Triggered', value: :status_asc
|
||||
value 'STATUS_DESC', 'Status by order: Triggered > Acknowledged > Resolved > Ignored', value: :status_desc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module OnboardingExperimentHelper
|
||||
def allow_access_to_onboarding?
|
||||
::Gitlab.dev_env_or_com? && Feature.enabled?(:user_onboarding)
|
||||
end
|
||||
end
|
||||
|
||||
OnboardingExperimentHelper.prepend_if_ee('EE::OnboardingExperimentHelper')
|
||||
|
|
@ -121,8 +121,16 @@ module AlertManagement
|
|||
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
|
||||
scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) }
|
||||
scope :order_event_count, -> (sort_order) { order(events: sort_order) }
|
||||
scope :order_severity, -> (sort_order) { order(severity: sort_order) }
|
||||
scope :order_status, -> (sort_order) { order(status: sort_order) }
|
||||
|
||||
# Ascending sort order sorts severity from less critical to more critical.
|
||||
# Descending sort order sorts severity from more critical to less critical.
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
|
||||
scope :order_severity, -> (sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
|
||||
|
||||
# Ascending sort order sorts statuses: Ignored > Resolved > Acknowledged > Triggered
|
||||
# Descending sort order sorts statuses: Triggered > Acknowledged > Resolved > Ignored
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
|
||||
scope :order_status, -> (sort_order) { order(status: sort_order == :asc ? :desc : :asc) }
|
||||
|
||||
scope :counts_by_status, -> { group(:status).count }
|
||||
scope :counts_by_project_id, -> { group(:project_id).count }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Clusters
|
||||
module Applications
|
||||
class Runner < ApplicationRecord
|
||||
VERSION = '0.18.0'
|
||||
VERSION = '0.18.1'
|
||||
|
||||
self.table_name = 'clusters_applications_runners'
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ class MergeRequest < ApplicationRecord
|
|||
|
||||
after_create :ensure_merge_request_diff
|
||||
after_update :clear_memoized_shas
|
||||
after_update :clear_memoized_source_branch_exists
|
||||
after_update :reload_diff_if_branch_changed
|
||||
after_commit :ensure_metrics, on: [:create, :update], unless: :importing?
|
||||
after_commit :expire_etag_cache, unless: :importing?
|
||||
|
|
@ -862,6 +863,10 @@ class MergeRequest < ApplicationRecord
|
|||
clear_memoization(:target_branch_head)
|
||||
end
|
||||
|
||||
def clear_memoized_source_branch_exists
|
||||
clear_memoization(:source_branch_exists)
|
||||
end
|
||||
|
||||
def reload_diff_if_branch_changed
|
||||
if (saved_change_to_source_branch? || saved_change_to_target_branch?) &&
|
||||
(source_branch_head && target_branch_head)
|
||||
|
|
@ -1109,9 +1114,17 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
|
||||
def source_branch_exists?
|
||||
return false unless self.source_project
|
||||
if Feature.enabled?(:memoize_source_branch_merge_request, project)
|
||||
strong_memoize(:source_branch_exists) do
|
||||
next false unless self.source_project
|
||||
|
||||
self.source_project.repository.branch_exists?(self.source_branch)
|
||||
self.source_project.repository.branch_exists?(self.source_branch)
|
||||
end
|
||||
else
|
||||
return false unless self.source_project
|
||||
|
||||
self.source_project.repository.branch_exists?(self.source_branch)
|
||||
end
|
||||
end
|
||||
|
||||
def target_branch_exists?
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
.col.form-group
|
||||
= f.label :title, _('Title'), class: 'label-bold'
|
||||
= f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
|
||||
%p.form-text.text-muted= s_('Profiles|Give your individual key a title')
|
||||
%p.form-text.text-muted= s_('Profiles|Give your individual key a title. This will be publically visible.')
|
||||
|
||||
.col.form-group
|
||||
= f.label :expires_at, s_('Profiles|Expires at'), class: 'label-bold'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Further improve the performance for loading large diffs on a Merge request
|
||||
merge_request: 34516
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change the sort order for alert severity and status.
|
||||
merge_request: 35774
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add note about SSH key title being public information
|
||||
merge_request: 35574
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Minor UI fixes for Issue page in dark mode
|
||||
merge_request: 35395
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update GitLab Runner Helm Chart to 0.18.1
|
||||
merge_request: 35712
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -395,12 +395,12 @@ enum AlertManagementAlertSort {
|
|||
EVENT_COUNT_DESC
|
||||
|
||||
"""
|
||||
Severity by ascending order
|
||||
Severity from less critical to more critical
|
||||
"""
|
||||
SEVERITY_ASC
|
||||
|
||||
"""
|
||||
Severity by descending order
|
||||
Severity from more critical to less critical
|
||||
"""
|
||||
SEVERITY_DESC
|
||||
|
||||
|
|
@ -415,12 +415,12 @@ enum AlertManagementAlertSort {
|
|||
STARTED_AT_DESC
|
||||
|
||||
"""
|
||||
Status by ascending order
|
||||
Status by order: Ignored > Resolved > Acknowledged > Triggered
|
||||
"""
|
||||
STATUS_ASC
|
||||
|
||||
"""
|
||||
Status by descending order
|
||||
Status by order: Triggered > Acknowledged > Resolved > Ignored
|
||||
"""
|
||||
STATUS_DESC
|
||||
|
||||
|
|
|
|||
|
|
@ -1103,25 +1103,25 @@
|
|||
},
|
||||
{
|
||||
"name": "SEVERITY_ASC",
|
||||
"description": "Severity by ascending order",
|
||||
"description": "Severity from less critical to more critical",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SEVERITY_DESC",
|
||||
"description": "Severity by descending order",
|
||||
"description": "Severity from more critical to less critical",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "STATUS_ASC",
|
||||
"description": "Status by ascending order",
|
||||
"description": "Status by order: Ignored > Resolved > Acknowledged > Triggered",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "STATUS_DESC",
|
||||
"description": "Status by descending order",
|
||||
"description": "Status by order: Triggered > Acknowledged > Resolved > Ignored",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -594,6 +594,7 @@ appear to be associated to any of the services running, since they all appear to
|
|||
| `ldap_enabled` | | | | | |
|
||||
| `mattermost_enabled` | | | | | |
|
||||
| `omniauth_enabled` | | | | | |
|
||||
| `prometheus_enabled` | | | | | Whether the bundled Prometheus is enabled |
|
||||
| `prometheus_metrics_enabled` | | | | | |
|
||||
| `reply_by_email_enabled` | | | | | |
|
||||
| `average` | `avg_cycle_analytics - code` | | | | |
|
||||
|
|
@ -671,6 +672,7 @@ appear to be associated to any of the services running, since they all appear to
|
|||
| `merge_requests_users` | `usage_activity_by_stage_monthly` | `create` | | | Unique count of users who used a merge request |
|
||||
| `duration_s` | `topology` | `enablement` | | | Time it took to collect topology data |
|
||||
| `application_requests_per_hour` | `topology` | `enablement` | | | Number of requests to the web application per hour |
|
||||
| `failures` | `topology` | `enablement` | | | Contains information about failed queries |
|
||||
| `nodes` | `topology` | `enablement` | | | The list of server nodes on which GitLab components are running |
|
||||
| `node_memory_total_bytes` | `topology > nodes` | `enablement` | | | The total available memory of this node |
|
||||
| `node_cpus` | `topology > nodes` | `enablement` | | | The number of CPU cores of this node |
|
||||
|
|
@ -723,6 +725,7 @@ The following is example content of the Usage Ping payload.
|
|||
"ldap_enabled": false,
|
||||
"mattermost_enabled": false,
|
||||
"omniauth_enabled": true,
|
||||
"prometheus_enabled": false,
|
||||
"prometheus_metrics_enabled": false,
|
||||
"reply_by_email_enabled": "incoming+%{key}@incoming.gitlab.com",
|
||||
"signup_enabled": true,
|
||||
|
|
@ -879,6 +882,7 @@ The following is example content of the Usage Ping payload.
|
|||
"topology": {
|
||||
"duration_s": 0.013836685999194742,
|
||||
"application_requests_per_hour": 4224,
|
||||
"failures": [],
|
||||
"nodes": [
|
||||
{
|
||||
"node_memory_total_bytes": 33269903360,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ Using Credentials inventory, GitLab administrators can see all the personal acce
|
|||
- Who they belong to.
|
||||
- Their access scope.
|
||||
- Their usage pattern.
|
||||
- When they expire.
|
||||
- When they were revoked.
|
||||
- When they expire. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214809) in GitLab 13.2.
|
||||
- When they were revoked. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214809) in GitLab 13.2.
|
||||
|
||||
To access the Credentials inventory, navigate to **Admin Area > Credentials**.
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,60 @@ it. This is because squashing is only available when accepting a merge request,
|
|||
so a merge request may need to be rebased before squashing, even though
|
||||
squashing can itself be considered equivalent to rebasing.
|
||||
|
||||
## Squash Commits Options
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17613) in GitLab 13.2.
|
||||
> - It's deployed behind a feature flag, disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's not recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-squash-commit-options-core-only). **(CORE ONLY)**
|
||||
|
||||
With Squash Commits Options you can configure the behavior of Squash and Merge for your project.
|
||||
To set it up, navigate to your project's **Settings > General** and expand **Merge requests**.
|
||||
You will find the following options to choose, which will affect existing and new merge requests
|
||||
submitted to your project:
|
||||
|
||||
- **Do not allow**: users cannot use Squash and Merge to squash all the commits immediately before
|
||||
merging. The checkbox to enable or disable it will be unchecked and hidden from the users.
|
||||
- **Allow**: users will have the option to enable Squash and Merge on a merge request basis.
|
||||
The checkbox will be unchecked (disabled) by default, but and the user is allowed to enable it.
|
||||
- **Encourage**: users will have the option to enable Squash and Merge on a merge request basis.
|
||||
The checkbox will be checked (enabled) by default to encourage its use, but the user is allowed to
|
||||
disable it.
|
||||
- **Require**: Squash and Merge is enabled for all merge requests, so it will always be performed.
|
||||
The checkbox to enable or disable it will be checked and hidden from the users.
|
||||
|
||||
The Squash and Merge checkbox is displayed when you create a merge request and when you edit the description of an existing one, except when Squash Commit Options is set to **Do not allow** or **Require**.
|
||||
|
||||
NOTE: **Note:**
|
||||
If your project is set to **Do not allow** Squash and Merge, the users still have the option to
|
||||
squash commits locally through the command line and force-push to their remote branch before merging.
|
||||
|
||||
### Enable or disable Squash Commit Options **(CORE ONLY)**
|
||||
|
||||
Squash Commit Options is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it for your instance. Squash Commit Options can be enabled or disabled per-project.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
# Instance-wide
|
||||
Feature.enable(:squash_options)
|
||||
# or by project
|
||||
Feature.enable(:squash_options, Project.find(<project id>))
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
# Instance-wide
|
||||
Feature.enable(:squash_options)
|
||||
# or by project
|
||||
Feature.disable(:squash_options, Project.find(<project id>))
|
||||
```
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ module Gitlab
|
|||
class PrometheusClient
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
Error = Class.new(StandardError)
|
||||
ConnectionError = Class.new(Gitlab::PrometheusClient::Error)
|
||||
UnexpectedResponseError = Class.new(Gitlab::PrometheusClient::Error)
|
||||
QueryError = Class.new(Gitlab::PrometheusClient::Error)
|
||||
HEALTHY_RESPONSE = "Prometheus is Healthy.\n"
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ module Gitlab
|
|||
path = api_path(type)
|
||||
get(path, args)
|
||||
rescue Gitlab::HTTP::ResponseError => ex
|
||||
raise PrometheusClient::Error, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
raise PrometheusClient::ConnectionError, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
|
||||
handle_querying_api_response(ex.response)
|
||||
end
|
||||
|
|
@ -115,7 +117,7 @@ module Gitlab
|
|||
response = get(path, args)
|
||||
handle_querying_api_response(response)
|
||||
rescue Gitlab::HTTP::ResponseError => ex
|
||||
raise PrometheusClient::Error, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
raise PrometheusClient::ConnectionError, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
|
||||
handle_querying_api_response(ex.response)
|
||||
end
|
||||
|
|
@ -137,18 +139,18 @@ module Gitlab
|
|||
def get(path, args)
|
||||
Gitlab::HTTP.get(path, { query: args }.merge(http_options) )
|
||||
rescue SocketError
|
||||
raise PrometheusClient::Error, "Can't connect to #{api_url}"
|
||||
raise PrometheusClient::ConnectionError, "Can't connect to #{api_url}"
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
raise PrometheusClient::Error, "#{api_url} contains invalid SSL data"
|
||||
raise PrometheusClient::ConnectionError, "#{api_url} contains invalid SSL data"
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise PrometheusClient::Error, 'Connection refused'
|
||||
raise PrometheusClient::ConnectionError, 'Connection refused'
|
||||
end
|
||||
|
||||
def handle_management_api_response(response)
|
||||
if response.code == 200
|
||||
response.body
|
||||
else
|
||||
raise PrometheusClient::Error, "#{response.code} - #{response.body}"
|
||||
raise PrometheusClient::UnexpectedResponseError, "#{response.code} - #{response.body}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -156,7 +158,7 @@ module Gitlab
|
|||
response_code = response.try(:code)
|
||||
response_body = response.try(:body)
|
||||
|
||||
raise PrometheusClient::Error, "#{response_code} - #{response_body}" unless response_code
|
||||
raise PrometheusClient::UnexpectedResponseError, "#{response_code} - #{response_body}" unless response_code
|
||||
|
||||
json_data = parse_json(response_body) if [200, 400].include?(response_code)
|
||||
|
||||
|
|
@ -166,7 +168,7 @@ module Gitlab
|
|||
when 400
|
||||
raise PrometheusClient::QueryError, json_data['error'] || 'Bad data received'
|
||||
else
|
||||
raise PrometheusClient::Error, "#{response_code} - #{response_body}"
|
||||
raise PrometheusClient::UnexpectedResponseError, "#{response_code} - #{response_body}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -178,7 +180,7 @@ module Gitlab
|
|||
def parse_json(response_body)
|
||||
Gitlab::Json.parse(response_body, legacy_mode: true)
|
||||
rescue JSON::ParserError
|
||||
raise PrometheusClient::Error, 'Parsing response failed'
|
||||
raise PrometheusClient::UnexpectedResponseError, 'Parsing response failed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ module Gitlab
|
|||
class << self
|
||||
include Gitlab::Utils::UsageData
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include Gitlab::UsageDataConcerns::Topology
|
||||
|
||||
def data(force_refresh: false)
|
||||
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) do
|
||||
|
|
@ -210,6 +209,7 @@ module Gitlab
|
|||
ldap_enabled: alt_usage_data(fallback: nil) { Gitlab.config.ldap.enabled },
|
||||
mattermost_enabled: alt_usage_data(fallback: nil) { Gitlab.config.mattermost.enabled },
|
||||
omniauth_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth.omniauth_enabled? },
|
||||
prometheus_enabled: alt_usage_data(fallback: nil) { Gitlab::Prometheus::Internal.prometheus_enabled? },
|
||||
prometheus_metrics_enabled: alt_usage_data(fallback: nil) { Gitlab::Metrics.prometheus_metrics_enabled? },
|
||||
reply_by_email_enabled: alt_usage_data(fallback: nil) { Gitlab::IncomingEmail.enabled? },
|
||||
signup_enabled: alt_usage_data(fallback: nil) { Gitlab::CurrentSettings.allow_signup? },
|
||||
|
|
@ -303,6 +303,10 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def topology_usage_data
|
||||
Gitlab::UsageData::Topology.new.topology_usage_data
|
||||
end
|
||||
|
||||
def ingress_modsecurity_usage
|
||||
##
|
||||
# This method measures usage of the Modsecurity Web Application Firewall across the entire
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module UsageDataConcerns
|
||||
module Topology
|
||||
class UsageData
|
||||
class Topology
|
||||
include Gitlab::Utils::UsageData
|
||||
|
||||
JOB_TO_SERVICE_NAME = {
|
||||
|
|
@ -16,11 +16,20 @@ module Gitlab
|
|||
'node' => 'node-exporter'
|
||||
}.freeze
|
||||
|
||||
def topology_usage_data
|
||||
topology_data, duration = measure_duration do
|
||||
alt_usage_data(fallback: {}) { topology_fetch_all_data }
|
||||
CollectionFailure = Struct.new(:query, :error) do
|
||||
def to_h
|
||||
{ query => error }
|
||||
end
|
||||
{ topology: topology_data.merge(duration_s: duration) }
|
||||
end
|
||||
|
||||
def topology_usage_data
|
||||
@failures = []
|
||||
topology_data, duration = measure_duration { topology_fetch_all_data }
|
||||
{
|
||||
topology: topology_data
|
||||
.merge(duration_s: duration)
|
||||
.merge(failures: @failures.map(&:to_h))
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -32,10 +41,17 @@ module Gitlab
|
|||
nodes: topology_node_data(client)
|
||||
}.compact
|
||||
end
|
||||
rescue => e
|
||||
@failures << CollectionFailure.new('other', e.class.to_s)
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
def topology_app_requests_per_hour(client)
|
||||
result = client.query(one_week_average('gitlab_usage_ping:ops:rate5m')).first
|
||||
result = query_safely('gitlab_usage_ping:ops:rate5m', 'app_requests', fallback: nil) do |query|
|
||||
client.query(one_week_average(query)).first
|
||||
end
|
||||
|
||||
return unless result
|
||||
|
||||
# the metric is recorded as a per-second rate
|
||||
|
|
@ -62,11 +78,15 @@ module Gitlab
|
|||
end
|
||||
|
||||
def topology_node_memory(client)
|
||||
aggregate_by_instance(client, 'gitlab_usage_ping:node_memory_total_bytes:avg')
|
||||
query_safely('gitlab_usage_ping:node_memory_total_bytes:avg', 'node_memory', fallback: {}) do |query|
|
||||
aggregate_by_instance(client, query)
|
||||
end
|
||||
end
|
||||
|
||||
def topology_node_cpus(client)
|
||||
aggregate_by_instance(client, 'gitlab_usage_ping:node_cpus:count')
|
||||
query_safely('gitlab_usage_ping:node_cpus:count', 'node_cpus', fallback: {}) do |query|
|
||||
aggregate_by_instance(client, query)
|
||||
end
|
||||
end
|
||||
|
||||
def topology_all_service_memory(client)
|
||||
|
|
@ -78,19 +98,39 @@ module Gitlab
|
|||
end
|
||||
|
||||
def topology_service_memory_rss(client)
|
||||
aggregate_by_labels(client, 'gitlab_usage_ping:node_service_process_resident_memory_bytes:avg')
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_resident_memory_bytes:avg', 'service_rss', fallback: []
|
||||
) { |query| aggregate_by_labels(client, query) }
|
||||
end
|
||||
|
||||
def topology_service_memory_uss(client)
|
||||
aggregate_by_labels(client, 'gitlab_usage_ping:node_service_process_unique_memory_bytes:avg')
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_unique_memory_bytes:avg', 'service_uss', fallback: []
|
||||
) { |query| aggregate_by_labels(client, query) }
|
||||
end
|
||||
|
||||
def topology_service_memory_pss(client)
|
||||
aggregate_by_labels(client, 'gitlab_usage_ping:node_service_process_proportional_memory_bytes:avg')
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process_proportional_memory_bytes:avg', 'service_pss', fallback: []
|
||||
) { |query| aggregate_by_labels(client, query) }
|
||||
end
|
||||
|
||||
def topology_all_service_process_count(client)
|
||||
aggregate_by_labels(client, 'gitlab_usage_ping:node_service_process:count')
|
||||
query_safely(
|
||||
'gitlab_usage_ping:node_service_process:count', 'service_process_count', fallback: []
|
||||
) { |query| aggregate_by_labels(client, query) }
|
||||
end
|
||||
|
||||
def query_safely(query, query_name, fallback:)
|
||||
result = yield query
|
||||
|
||||
return result if result.present?
|
||||
|
||||
@failures << CollectionFailure.new(query_name, 'empty_result')
|
||||
fallback
|
||||
rescue => e
|
||||
@failures << CollectionFailure.new(query_name, e.class.to_s)
|
||||
fallback
|
||||
end
|
||||
|
||||
def topology_node_services(instance, all_process_counts, all_process_memory)
|
||||
|
|
@ -2238,6 +2238,9 @@ msgstr ""
|
|||
msgid "Allow users to request access (if visibility is public or internal)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Allowed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Allowed Geo IP"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -7441,6 +7444,9 @@ msgstr ""
|
|||
msgid "Deletion pending. This project will be removed on %{date}. Repository and other project resources are read-only."
|
||||
msgstr ""
|
||||
|
||||
msgid "Denied"
|
||||
msgstr ""
|
||||
|
||||
msgid "Denied authorization of chat nickname %{user_name}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -13466,6 +13472,9 @@ msgstr ""
|
|||
msgid "Licenses|%{remainingComponentsCount} more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Licenses|Acceptable license to be used in the project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Licenses|Component"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -13478,6 +13487,9 @@ msgstr ""
|
|||
msgid "Licenses|Detected licenses that are out-of-compliance with the project's assigned policies"
|
||||
msgstr ""
|
||||
|
||||
msgid "Licenses|Disallow Merge request if detected and will instruct the developer to remove"
|
||||
msgstr ""
|
||||
|
||||
msgid "Licenses|Displays licenses detected in the project, based on the %{linkStart}latest successful%{linkEnd} scan"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16869,9 +16881,6 @@ msgstr ""
|
|||
msgid "Please type %{phrase_code} to proceed or close this modal to cancel."
|
||||
msgstr ""
|
||||
|
||||
msgid "Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version."
|
||||
msgstr ""
|
||||
|
||||
msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17250,7 +17259,7 @@ msgstr ""
|
|||
msgid "Profiles|Full name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Give your individual key a title"
|
||||
msgid "Profiles|Give your individual key a title. This will be publically visible."
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Include private contributions on my profile"
|
||||
|
|
|
|||
|
|
@ -75,10 +75,30 @@ FactoryBot.define do
|
|||
without_ended_at
|
||||
end
|
||||
|
||||
trait :low_severity do
|
||||
trait :critical do
|
||||
severity { 'critical' }
|
||||
end
|
||||
|
||||
trait :high do
|
||||
severity { 'high' }
|
||||
end
|
||||
|
||||
trait :medium do
|
||||
severity { 'medium' }
|
||||
end
|
||||
|
||||
trait :low do
|
||||
severity { 'low' }
|
||||
end
|
||||
|
||||
trait :info do
|
||||
severity { 'info' }
|
||||
end
|
||||
|
||||
trait :unknown do
|
||||
severity { 'unknown' }
|
||||
end
|
||||
|
||||
trait :prometheus do
|
||||
monitoring_tool { Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus] }
|
||||
end
|
||||
|
|
@ -91,7 +111,7 @@ FactoryBot.define do
|
|||
with_monitoring_tool
|
||||
with_host
|
||||
with_description
|
||||
low_severity
|
||||
low
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
|
|||
let(:params) { {} }
|
||||
|
||||
describe '#execute' do
|
||||
subject { described_class.new(current_user, project, params).execute }
|
||||
subject(:execute) { described_class.new(current_user, project, params).execute }
|
||||
|
||||
context 'user is not a developer or above' do
|
||||
it { is_expected.to be_empty }
|
||||
|
|
@ -144,81 +144,55 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
|
|||
end
|
||||
|
||||
context 'when sorting by severity' do
|
||||
let_it_be(:alert_critical) { create(:alert_management_alert, project: project, severity: :critical) }
|
||||
let_it_be(:alert_high) { create(:alert_management_alert, project: project, severity: :high) }
|
||||
let_it_be(:alert_medium) { create(:alert_management_alert, project: project, severity: :medium) }
|
||||
let_it_be(:alert_low) { create(:alert_management_alert, project: project, severity: :low) }
|
||||
let_it_be(:alert_info) { create(:alert_management_alert, project: project, severity: :info) }
|
||||
let_it_be(:alert_unknown) { create(:alert_management_alert, project: project, severity: :unknown) }
|
||||
let_it_be(:alert_critical) { create(:alert_management_alert, :critical, project: project) }
|
||||
let_it_be(:alert_high) { create(:alert_management_alert, :high, project: project) }
|
||||
let_it_be(:alert_medium) { create(:alert_management_alert, :medium, project: project) }
|
||||
let_it_be(:alert_low) { create(:alert_management_alert, :low, project: project) }
|
||||
let_it_be(:alert_info) { create(:alert_management_alert, :info, project: project) }
|
||||
let_it_be(:alert_unknown) { create(:alert_management_alert, :unknown, project: project) }
|
||||
|
||||
context 'sorts alerts ascending' do
|
||||
context 'with ascending sort order' do
|
||||
let(:params) { { sort: 'severity_asc' } }
|
||||
|
||||
it do
|
||||
is_expected.to eq [
|
||||
alert_2,
|
||||
alert_critical,
|
||||
alert_1,
|
||||
alert_high,
|
||||
alert_medium,
|
||||
alert_low,
|
||||
alert_info,
|
||||
alert_unknown
|
||||
]
|
||||
it 'sorts alerts by severity from less critical to more critical' do
|
||||
expect(execute.pluck(:severity).uniq).to eq(%w(unknown info low medium high critical))
|
||||
end
|
||||
end
|
||||
|
||||
context 'sorts alerts descending' do
|
||||
context 'with descending sort order' do
|
||||
let(:params) { { sort: 'severity_desc' } }
|
||||
|
||||
it do
|
||||
is_expected.to eq [
|
||||
alert_unknown,
|
||||
alert_info,
|
||||
alert_low,
|
||||
alert_medium,
|
||||
alert_1,
|
||||
alert_high,
|
||||
alert_critical,
|
||||
alert_2
|
||||
]
|
||||
it 'sorts alerts by severity from more critical to less critical' do
|
||||
expect(execute.pluck(:severity).uniq).to eq(%w(critical high medium low info unknown))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sorting by status' do
|
||||
let(:statuses) { AlertManagement::Alert::STATUSES }
|
||||
let(:triggered) { statuses[:triggered] }
|
||||
let(:acknowledged) { statuses[:acknowledged] }
|
||||
let(:resolved) { statuses[:resolved] }
|
||||
let(:ignored) { statuses[:ignored] }
|
||||
|
||||
let_it_be(:alert_triggered) { create(:alert_management_alert, project: project) }
|
||||
let_it_be(:alert_acknowledged) { create(:alert_management_alert, :acknowledged, project: project) }
|
||||
let_it_be(:alert_resolved) { create(:alert_management_alert, :resolved, project: project) }
|
||||
let_it_be(:alert_ignored) { create(:alert_management_alert, :ignored, project: project) }
|
||||
|
||||
context 'sorts alerts ascending' do
|
||||
context 'with ascending sort order' do
|
||||
let(:params) { { sort: 'status_asc' } }
|
||||
|
||||
it do
|
||||
is_expected.to eq [
|
||||
alert_triggered,
|
||||
alert_acknowledged,
|
||||
alert_1,
|
||||
alert_resolved,
|
||||
alert_2,
|
||||
alert_ignored
|
||||
]
|
||||
it 'sorts by status: Ignored > Resolved > Acknowledged > Triggered' do
|
||||
expect(execute.map(&:status).uniq).to eq([ignored, resolved, acknowledged, triggered])
|
||||
end
|
||||
end
|
||||
|
||||
context 'sorts alerts descending' do
|
||||
context 'with descending sort order' do
|
||||
let(:params) { { sort: 'status_desc' } }
|
||||
|
||||
it do
|
||||
is_expected.to eq [
|
||||
alert_2,
|
||||
alert_ignored,
|
||||
alert_1,
|
||||
alert_resolved,
|
||||
alert_acknowledged,
|
||||
alert_triggered
|
||||
]
|
||||
it 'sorts by status: Triggered > Acknowledged > Resolved > Ignored' do
|
||||
expect(execute.map(&:status).uniq).to eq([triggered, acknowledged, resolved, ignored])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,324 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Resizable Skeleton Loader default setup renders the bars, labels, and grid with correct position, size, and rx percentages 1`] = `
|
||||
<gl-skeleton-loader-stub
|
||||
baseurl=""
|
||||
height="130"
|
||||
preserveaspectratio="xMidYMid meet"
|
||||
width="400"
|
||||
>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="30%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="60%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="90%"
|
||||
/>
|
||||
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="5.875%"
|
||||
y="85%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="7%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="17.625%"
|
||||
y="83%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="9%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="29.375%"
|
||||
y="81%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="14%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="41.125%"
|
||||
y="76%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="21%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="52.875%"
|
||||
y="69%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="35%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="64.625%"
|
||||
y="55%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="50%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="76.375%"
|
||||
y="40%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="80%"
|
||||
rx="0.4%"
|
||||
width="6%"
|
||||
x="88.125%"
|
||||
y="10%"
|
||||
/>
|
||||
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="6.875%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="18.625%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="30.375%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="42.125%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="53.875%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="65.625%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="77.375%"
|
||||
y="95%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="5%"
|
||||
rx="0.4%"
|
||||
width="4%"
|
||||
x="89.125%"
|
||||
y="95%"
|
||||
/>
|
||||
</gl-skeleton-loader-stub>
|
||||
`;
|
||||
|
||||
exports[`Resizable Skeleton Loader with custom settings renders the correct position, and size percentages for bars and labels with different settings 1`] = `
|
||||
<gl-skeleton-loader-stub
|
||||
baseurl=""
|
||||
height="130"
|
||||
preserveaspectratio="xMidYMid meet"
|
||||
uniquekey=""
|
||||
width="400"
|
||||
>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="30%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="60%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-grid"
|
||||
height="1px"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="90%"
|
||||
/>
|
||||
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="5%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="6.0625%"
|
||||
y="85%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="7%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="18.1875%"
|
||||
y="83%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="9%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="30.3125%"
|
||||
y="81%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="14%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="42.4375%"
|
||||
y="76%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="21%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="54.5625%"
|
||||
y="69%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="35%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="66.6875%"
|
||||
y="55%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="50%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="78.8125%"
|
||||
y="40%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-bar"
|
||||
height="80%"
|
||||
rx="0.6%"
|
||||
width="3%"
|
||||
x="90.9375%"
|
||||
y="10%"
|
||||
/>
|
||||
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="4.0625%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="16.1875%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="28.3125%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="40.4375%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="52.5625%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="64.6875%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="76.8125%"
|
||||
y="98%"
|
||||
/>
|
||||
<rect
|
||||
data-testid="skeleton-chart-label"
|
||||
height="2%"
|
||||
rx="0.6%"
|
||||
width="7%"
|
||||
x="88.9375%"
|
||||
y="98%"
|
||||
/>
|
||||
</gl-skeleton-loader-stub>
|
||||
`;
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
|
||||
|
||||
describe('Resizable Skeleton Loader', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (propsData = {}) => {
|
||||
wrapper = shallowMount(ChartSkeletonLoader, {
|
||||
propsData,
|
||||
});
|
||||
};
|
||||
|
||||
const verifyElementsPresence = () => {
|
||||
const gridItems = wrapper.findAll('[data-testid="skeleton-chart-grid"]').wrappers;
|
||||
const barItems = wrapper.findAll('[data-testid="skeleton-chart-bar"]').wrappers;
|
||||
const labelItems = wrapper.findAll('[data-testid="skeleton-chart-label"]').wrappers;
|
||||
expect(gridItems.length).toBe(3);
|
||||
expect(barItems.length).toBe(8);
|
||||
expect(labelItems.length).toBe(8);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper?.destroy) {
|
||||
wrapper.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
describe('default setup', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ uniqueKey: null });
|
||||
});
|
||||
|
||||
it('renders the bars, labels, and grid with correct position, size, and rx percentages', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders the correct number of grid items, bars, and labels', () => {
|
||||
verifyElementsPresence();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with custom settings', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ uniqueKey: '', rx: 0.6, barWidth: 3, labelWidth: 7, labelHeight: 2 });
|
||||
});
|
||||
|
||||
it('renders the correct position, and size percentages for bars and labels with different settings', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders the correct number of grid items, bars, and labels', () => {
|
||||
verifyElementsPresence();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe OnboardingExperimentHelper, type: :helper do
|
||||
describe '.allow_access_to_onboarding?' do
|
||||
context "when we're not gitlab.com or dev env" do
|
||||
it 'returns false' do
|
||||
allow(::Gitlab).to receive(:dev_env_or_com?).and_return(false)
|
||||
|
||||
expect(helper.allow_access_to_onboarding?).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "when we're gitlab.com or dev env" do
|
||||
before do
|
||||
allow(::Gitlab).to receive(:dev_env_or_com?).and_return(true)
|
||||
end
|
||||
|
||||
context 'and the :user_onboarding feature is not enabled' do
|
||||
it 'returns false' do
|
||||
stub_feature_flags(user_onboarding: false)
|
||||
|
||||
expect(helper.allow_access_to_onboarding?).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the :user_onboarding feature is enabled' do
|
||||
it 'returns true' do
|
||||
stub_feature_flags(user_onboarding: true)
|
||||
allow(helper).to receive(:current_user).and_return(create(:user))
|
||||
|
||||
expect(helper.allow_access_to_onboarding?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -32,7 +32,7 @@ RSpec.describe Gitlab::PrometheusClient do
|
|||
it 'raises error when status code not 200' do
|
||||
stub_request(:get, subject.health_url).to_return(status: 500, body: '')
|
||||
|
||||
expect { subject.healthy? }.to raise_error(Gitlab::PrometheusClient::Error)
|
||||
expect { subject.healthy? }.to raise_error(Gitlab::PrometheusClient::UnexpectedResponseError)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -41,41 +41,41 @@ RSpec.describe Gitlab::PrometheusClient do
|
|||
# - execute_query: A query call
|
||||
shared_examples 'failure response' do
|
||||
context 'when request returns 400 with an error message' do
|
||||
it 'raises a Gitlab::PrometheusClient::Error error' do
|
||||
it 'raises a Gitlab::PrometheusClient::QueryError error' do
|
||||
req_stub = stub_prometheus_request(query_url, status: 400, body: { error: 'bar!' })
|
||||
|
||||
expect { execute_query }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, 'bar!')
|
||||
.to raise_error(Gitlab::PrometheusClient::QueryError, 'bar!')
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'when request returns 400 without an error message' do
|
||||
it 'raises a Gitlab::PrometheusClient::Error error' do
|
||||
it 'raises a Gitlab::PrometheusClient::QueryError error' do
|
||||
req_stub = stub_prometheus_request(query_url, status: 400)
|
||||
|
||||
expect { execute_query }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, 'Bad data received')
|
||||
.to raise_error(Gitlab::PrometheusClient::QueryError, 'Bad data received')
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'when request returns 500' do
|
||||
it 'raises a Gitlab::PrometheusClient::Error error' do
|
||||
it 'raises a Gitlab::PrometheusClient::UnexpectedResponseError error' do
|
||||
req_stub = stub_prometheus_request(query_url, status: 500, body: { message: 'FAIL!' })
|
||||
|
||||
expect { execute_query }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, '500 - {"message":"FAIL!"}')
|
||||
.to raise_error(Gitlab::PrometheusClient::UnexpectedResponseError, '500 - {"message":"FAIL!"}')
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'when request returns non json data' do
|
||||
it 'raises a Gitlab::PrometheusClient::Error error' do
|
||||
it 'raises a Gitlab::PrometheusClient::UnexpectedResponseError error' do
|
||||
req_stub = stub_prometheus_request(query_url, status: 200, body: 'not json')
|
||||
|
||||
expect { execute_query }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, 'Parsing response failed')
|
||||
.to raise_error(Gitlab::PrometheusClient::UnexpectedResponseError, 'Parsing response failed')
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
|
@ -85,35 +85,35 @@ RSpec.describe Gitlab::PrometheusClient do
|
|||
let(:prometheus_url) {"https://prometheus.invalid.example.com/api/v1/query?query=1"}
|
||||
|
||||
shared_examples 'exceptions are raised' do
|
||||
it 'raises a Gitlab::PrometheusClient::Error error when a SocketError is rescued' do
|
||||
it 'raises a Gitlab::PrometheusClient::ConnectionError error when a SocketError is rescued' do
|
||||
req_stub = stub_prometheus_request_with_exception(prometheus_url, SocketError)
|
||||
|
||||
expect { subject }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, "Can't connect to #{prometheus_url}")
|
||||
.to raise_error(Gitlab::PrometheusClient::ConnectionError, "Can't connect to #{prometheus_url}")
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'raises a Gitlab::PrometheusClient::Error error when a SSLError is rescued' do
|
||||
it 'raises a Gitlab::PrometheusClient::ConnectionError error when a SSLError is rescued' do
|
||||
req_stub = stub_prometheus_request_with_exception(prometheus_url, OpenSSL::SSL::SSLError)
|
||||
|
||||
expect { subject }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, "#{prometheus_url} contains invalid SSL data")
|
||||
.to raise_error(Gitlab::PrometheusClient::ConnectionError, "#{prometheus_url} contains invalid SSL data")
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'raises a Gitlab::PrometheusClient::Error error when a Gitlab::HTTP::ResponseError is rescued' do
|
||||
it 'raises a Gitlab::PrometheusClient::ConnectionError error when a Gitlab::HTTP::ResponseError is rescued' do
|
||||
req_stub = stub_prometheus_request_with_exception(prometheus_url, Gitlab::HTTP::ResponseError)
|
||||
|
||||
expect { subject }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, "Network connection error")
|
||||
.to raise_error(Gitlab::PrometheusClient::ConnectionError, "Network connection error")
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'raises a Gitlab::PrometheusClient::Error error when a Gitlab::HTTP::ResponseError with a code is rescued' do
|
||||
it 'raises a Gitlab::PrometheusClient::ConnectionError error when a Gitlab::HTTP::ResponseError with a code is rescued' do
|
||||
req_stub = stub_prometheus_request_with_exception(prometheus_url, Gitlab::HTTP::ResponseError.new(code: 400))
|
||||
|
||||
expect { subject }
|
||||
.to raise_error(Gitlab::PrometheusClient::Error, "Network connection error")
|
||||
.to raise_error(Gitlab::PrometheusClient::ConnectionError, "Network connection error")
|
||||
expect(req_stub).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
|
@ -400,9 +400,9 @@ RSpec.describe Gitlab::PrometheusClient do
|
|||
context "without response code" do
|
||||
let(:response_error) { Gitlab::HTTP::ResponseError }
|
||||
|
||||
it 'raises PrometheusClient::Error' do
|
||||
it 'raises PrometheusClient::ConnectionError' do
|
||||
expect { subject.proxy('query', { query: prometheus_query }) }.to(
|
||||
raise_error(Gitlab::PrometheusClient::Error, 'Network connection error')
|
||||
raise_error(Gitlab::PrometheusClient::ConnectionError, 'Network connection error')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::UsageDataConcerns::Topology do
|
||||
RSpec.describe Gitlab::UsageData::Topology do
|
||||
include UsageDataHelpers
|
||||
|
||||
describe '#topology_usage_data' do
|
||||
subject { Class.new.extend(described_class).topology_usage_data }
|
||||
subject { described_class.new.topology_usage_data }
|
||||
|
||||
before do
|
||||
# this pins down time shifts when benchmarking durations
|
||||
|
|
@ -34,6 +34,7 @@ RSpec.describe Gitlab::UsageDataConcerns::Topology do
|
|||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
application_requests_per_hour: 36,
|
||||
failures: [],
|
||||
nodes: [
|
||||
{
|
||||
node_memory_total_bytes: 512,
|
||||
|
|
@ -76,7 +77,7 @@ RSpec.describe Gitlab::UsageDataConcerns::Topology do
|
|||
end
|
||||
|
||||
context 'and some node memory metrics are missing' do
|
||||
it 'removes the respective entries' do
|
||||
it 'removes the respective entries and includes the failures' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query(result: []),
|
||||
receive_node_memory_query(result: []),
|
||||
|
|
@ -89,6 +90,12 @@ RSpec.describe Gitlab::UsageDataConcerns::Topology do
|
|||
|
||||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'empty_result' },
|
||||
{ 'node_memory' => 'empty_result' },
|
||||
{ 'service_rss' => 'empty_result' },
|
||||
{ 'service_uss' => 'empty_result' }
|
||||
],
|
||||
nodes: [
|
||||
{
|
||||
node_cpus: 16,
|
||||
|
|
@ -123,31 +130,50 @@ RSpec.describe Gitlab::UsageDataConcerns::Topology do
|
|||
end
|
||||
end
|
||||
|
||||
context 'and no results are found' do
|
||||
it 'does not report anything' do
|
||||
expect_prometheus_api_to receive(:query).at_least(:once).and_return({})
|
||||
context 'and an error is raised when querying Prometheus' do
|
||||
it 'returns empty result with failures' do
|
||||
expect_prometheus_api_to receive(:query)
|
||||
.at_least(:once)
|
||||
.and_raise(Gitlab::PrometheusClient::ConnectionError)
|
||||
|
||||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'node_memory' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'node_cpus' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'service_rss' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'service_uss' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'service_pss' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'service_process_count' => 'Gitlab::PrometheusClient::ConnectionError' }
|
||||
],
|
||||
nodes: []
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context 'and a connection error is raised' do
|
||||
it 'does not report anything' do
|
||||
expect_prometheus_api_to receive(:query).and_raise('Connection failed')
|
||||
|
||||
expect(subject[:topology]).to eq({ duration_s: 0 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when embedded Prometheus server is disabled' do
|
||||
it 'does not report anything' do
|
||||
it 'returns empty result with no failures' do
|
||||
expect(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(false)
|
||||
|
||||
expect(subject[:topology]).to eq({ duration_s: 0 })
|
||||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
failures: []
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when top-level function raises error' do
|
||||
it 'returns empty result with generic failure' do
|
||||
allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_raise(RuntimeError)
|
||||
|
||||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'other' => 'RuntimeError' }
|
||||
]
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -347,6 +347,20 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(subject[:grafana_link_enabled]).to eq(Gitlab::CurrentSettings.grafana_enabled?)
|
||||
end
|
||||
|
||||
context 'with embedded Prometheus' do
|
||||
it 'returns true when embedded Prometheus is enabled' do
|
||||
allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(true)
|
||||
|
||||
expect(subject[:prometheus_enabled]).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when embedded Prometheus is disabled' do
|
||||
allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(false)
|
||||
|
||||
expect(subject[:prometheus_enabled]).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with embedded grafana' do
|
||||
it 'returns true when embedded grafana is enabled' do
|
||||
stub_application_setting(grafana_enabled: true)
|
||||
|
|
|
|||
|
|
@ -1217,6 +1217,59 @@ RSpec.describe MergeRequest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#source_branch_exists?" do
|
||||
let(:merge_request) { subject }
|
||||
let(:repository) { merge_request.source_project.repository }
|
||||
|
||||
context 'when memoize_source_branch_merge_request feature is enabled' do
|
||||
before do
|
||||
stub_feature_flags(memoize_source_branch_merge_request: true)
|
||||
end
|
||||
|
||||
context 'when the source project is set' do
|
||||
it 'memoizes the value and returns the result' do
|
||||
expect(repository).to receive(:branch_exists?).once.with(merge_request.source_branch).and_return(true)
|
||||
|
||||
2.times { expect(merge_request.source_branch_exists?).to eq(true) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the source project is not set' do
|
||||
before do
|
||||
merge_request.source_project = nil
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(merge_request.source_branch_exists?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when memoize_source_branch_merge_request feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(memoize_source_branch_merge_request: false)
|
||||
end
|
||||
|
||||
context 'when the source project is set' do
|
||||
it 'does not memoize the value and returns the result' do
|
||||
expect(repository).to receive(:branch_exists?).twice.with(merge_request.source_branch).and_return(true)
|
||||
|
||||
2.times { expect(merge_request.source_branch_exists?).to eq(true) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the source project is not set' do
|
||||
before do
|
||||
merge_request.source_project = nil
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(merge_request.source_branch_exists?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_merge_commit_message' do
|
||||
it 'includes merge information as the title' do
|
||||
request = build(:merge_request, source_branch: 'source', target_branch: 'target')
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching do
|
|||
|
||||
# result = { success: false, result: error }
|
||||
expect(result[:success]).to be_falsy
|
||||
expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::Error)
|
||||
expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::UnexpectedResponseError)
|
||||
|
||||
expect(redirect_req_stub).to have_been_requested
|
||||
expect(redirected_req_stub).not_to have_been_requested
|
||||
|
|
|
|||
|
|
@ -110,14 +110,14 @@ RSpec.describe 'getting Alert Management Alerts' do
|
|||
it_behaves_like 'a working graphql query'
|
||||
|
||||
it 'sorts in the correct order' do
|
||||
expect(iids).to eq [resolved_alert.iid.to_s, triggered_alert.iid.to_s]
|
||||
expect(iids).to eq [triggered_alert.iid.to_s, resolved_alert.iid.to_s]
|
||||
end
|
||||
|
||||
context 'ascending order' do
|
||||
let(:params) { 'sort: SEVERITY_ASC' }
|
||||
|
||||
it 'sorts in the correct order' do
|
||||
expect(iids).to eq [triggered_alert.iid.to_s, resolved_alert.iid.to_s]
|
||||
expect(iids).to eq [resolved_alert.iid.to_s, triggered_alert.iid.to_s]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ RSpec.describe MergeRequests::Conflicts::ListService do
|
|||
it 'returns a falsey value when one of the MR branches is missing' do
|
||||
merge_request = create_merge_request('conflict-resolvable')
|
||||
merge_request.project.repository.rm_branch(merge_request.author, 'conflict-resolvable')
|
||||
merge_request.clear_memoized_source_branch_exists
|
||||
|
||||
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue