Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1cf90c7008
commit
34ad6d995b
|
@ -54,6 +54,7 @@ review-deploy:
|
|||
extends:
|
||||
- .review-workflow-base
|
||||
- .review:rules:review-deploy
|
||||
retry: 2
|
||||
stage: review
|
||||
needs: ["review-build-cng"]
|
||||
resource_group: "review/${CI_COMMIT_REF_NAME}"
|
||||
|
|
|
@ -51,7 +51,6 @@ export default {
|
|||
</template>
|
||||
<div
|
||||
v-if="issuableCount > -1"
|
||||
data-testid="issuable-count-note"
|
||||
class="gl-justify-content-start gl-align-items-center gl-p-4 gl-border-b-solid gl-border-1 gl-border-gray-50"
|
||||
>
|
||||
<gl-icon name="check" class="gl-color-green-400" />
|
||||
|
|
|
@ -72,9 +72,6 @@ export default {
|
|||
importModalId() {
|
||||
return `${this.issuableType}-import-modal`;
|
||||
},
|
||||
importButtonText() {
|
||||
return this.showLabel ? this.$options.i18n.importIssuesText : null;
|
||||
},
|
||||
importButtonTooltipText() {
|
||||
return this.showLabel ? null : this.$options.i18n.importIssuesText;
|
||||
},
|
||||
|
@ -90,29 +87,25 @@ export default {
|
|||
<gl-button-group>
|
||||
<gl-button
|
||||
v-if="showExportButton"
|
||||
v-gl-tooltip.hover="$options.i18n.exportAsCsvButtonText"
|
||||
v-gl-tooltip="$options.i18n.exportAsCsvButtonText"
|
||||
v-gl-modal="exportModalId"
|
||||
icon="export"
|
||||
:aria-label="$options.i18n.exportAsCsvButtonText"
|
||||
data-qa-selector="export_as_csv_button"
|
||||
data-testid="export-csv-button"
|
||||
/>
|
||||
<gl-dropdown
|
||||
v-if="showImportButton"
|
||||
v-gl-tooltip.hover="importButtonTooltipText"
|
||||
v-gl-tooltip="importButtonTooltipText"
|
||||
data-qa-selector="import_issues_dropdown"
|
||||
data-testid="import-csv-dropdown"
|
||||
:text="importButtonText"
|
||||
:text="$options.i18n.importIssuesText"
|
||||
:text-sr-only="!showLabel"
|
||||
:icon="importButtonIcon"
|
||||
>
|
||||
<gl-dropdown-item v-gl-modal="importModalId" data-testid="import-csv-link">{{
|
||||
__('Import CSV')
|
||||
}}</gl-dropdown-item>
|
||||
<gl-dropdown-item v-gl-modal="importModalId">{{ __('Import CSV') }}</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="canEdit"
|
||||
:href="projectImportJiraPath"
|
||||
data-qa-selector="import_from_jira_link"
|
||||
data-testid="import-from-jira-link"
|
||||
>{{ __('Import from Jira') }}</gl-dropdown-item
|
||||
>
|
||||
</gl-dropdown>
|
||||
|
|
|
@ -48,13 +48,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-modal :modal-id="modalId" :title="__('Import issues')">
|
||||
<form
|
||||
ref="form"
|
||||
:action="importCsvIssuesPath"
|
||||
enctype="multipart/form-data"
|
||||
method="post"
|
||||
data-testid="import-csv-form"
|
||||
>
|
||||
<form ref="form" :action="importCsvIssuesPath" enctype="multipart/form-data" method="post">
|
||||
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
|
||||
<p>
|
||||
{{
|
||||
|
|
|
@ -91,7 +91,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button v-gl-modal="$options.modalId" variant="link" data-testid="issuable-email-modal-btn"
|
||||
<gl-button v-gl-modal="$options.modalId" variant="link"
|
||||
><gl-sprintf :message="__('Email a new %{name} to this project')"
|
||||
><template #name>{{ issuableName }}</template></gl-sprintf
|
||||
></gl-button
|
||||
|
@ -122,7 +122,6 @@ export default {
|
|||
:title="$options.i18n.sendEmail"
|
||||
:aria-label="$options.i18n.sendEmail"
|
||||
icon="mail"
|
||||
data-testid="mail-to-btn"
|
||||
/>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
|
@ -156,12 +155,7 @@ export default {
|
|||
/></gl-link>
|
||||
</template>
|
||||
<template #resetLink="{ content }">
|
||||
<gl-button
|
||||
variant="link"
|
||||
data-testid="incoming-email-token-reset"
|
||||
@click="resetIncomingEmailToken"
|
||||
>{{ content }}</gl-button
|
||||
>
|
||||
<gl-button variant="link" @click="resetIncomingEmailToken">{{ content }}</gl-button>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
|
|
|
@ -91,11 +91,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div :class="statusBoxClass" class="issuable-status-box status-box">
|
||||
<gl-icon
|
||||
:name="statusIconName"
|
||||
class="gl-display-block gl-sm-display-none!"
|
||||
data-testid="status-icon"
|
||||
/>
|
||||
<gl-icon :name="statusIconName" class="gl-display-block gl-sm-display-none!" />
|
||||
<span class="gl-display-none gl-sm-display-block">
|
||||
{{ statusHumanName }}
|
||||
</span>
|
||||
|
|
|
@ -484,6 +484,9 @@ export const setUrlParams = (
|
|||
searchParams.delete(key);
|
||||
} else if (Array.isArray(params[key])) {
|
||||
const keyName = railsArraySyntax ? `${key}[]` : key;
|
||||
if (params[key].length === 0) {
|
||||
searchParams.delete(keyName);
|
||||
} else {
|
||||
params[key].forEach((val, idx) => {
|
||||
if (idx === 0) {
|
||||
searchParams.set(keyName, val);
|
||||
|
@ -491,6 +494,7 @@ export const setUrlParams = (
|
|||
searchParams.append(keyName, val);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
.gfm-commit,
|
||||
.gfm-commit_range {
|
||||
@extend .commit-sha;
|
||||
@include gl-font-monospace;
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.gfm-project_member {
|
||||
|
|
|
@ -45,7 +45,6 @@ input[type='checkbox']:hover {
|
|||
margin: 0 8px;
|
||||
|
||||
form {
|
||||
@extend .form-control;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
width: $search-input-width;
|
||||
|
@ -139,7 +138,6 @@ input[type='checkbox']:hover {
|
|||
|
||||
&.search-active {
|
||||
form {
|
||||
@extend .form-control:focus;
|
||||
border-color: $blue-300;
|
||||
box-shadow: none;
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.gl-text-purple { color: $purple; }
|
||||
.gl-bg-purple-light { background-color: $purple-light; }
|
||||
|
||||
// move this to GitLab UI once onboarding experiment is considered a success
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.search.search-form{ data: { track_label: "navbar_search", track_event: "activate_form_input", track_value: "" } }
|
||||
= form_tag search_path, method: :get, class: 'form-inline' do |_f|
|
||||
= form_tag search_path, method: :get, class: 'form-inline form-control' do |_f|
|
||||
.search-input-container
|
||||
.search-input-wrap
|
||||
.dropdown{ data: { url: search_autocomplete_path } }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove UnicornCheck service
|
||||
merge_request: 62204
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Simplify error code handling for external pipeline validation
|
||||
merge_request: 61190
|
||||
author:
|
||||
type: changed
|
|
@ -7,8 +7,6 @@
|
|||
#
|
||||
# Follow-up the issue: https://gitlab.com/gitlab-org/gitlab/issues/34107
|
||||
|
||||
if Gitlab::Runtime.puma?
|
||||
Puma::Cluster.prepend(::Gitlab::Cluster::Mixins::PumaCluster)
|
||||
elsif Gitlab::Runtime.unicorn?
|
||||
Unicorn::HttpServer.prepend(::Gitlab::Cluster::Mixins::UnicornHttpServer)
|
||||
end
|
||||
return unless Gitlab::Runtime.puma?
|
||||
|
||||
Puma::Cluster.prepend(::Gitlab::Cluster::Mixins::PumaCluster)
|
||||
|
|
|
@ -17,7 +17,7 @@ data as payload. The response code from the external service determines if GitLa
|
|||
should accept or reject the pipeline. If the response is:
|
||||
|
||||
- `200`, the pipeline is accepted.
|
||||
- `4XX`, the pipeline is rejected.
|
||||
- `406`, the pipeline is rejected.
|
||||
- Other codes, the pipeline is accepted and logged.
|
||||
|
||||
If there's an error or the request times out, the pipeline is accepted.
|
||||
|
|
|
@ -45,6 +45,15 @@ You can select the branch in the version dropdown in the top left corner of GitL
|
|||
|
||||
If the highest number stable branch is unclear, check the [GitLab blog](https://about.gitlab.com/blog/) for installation guide links by version.
|
||||
|
||||
## Software requirements
|
||||
|
||||
| Software | Minimum version | Notes |
|
||||
| -------- | --------------- | ----- |
|
||||
| [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. |
|
||||
| [Go](#3-go) | `1.15` | |
|
||||
| [Git](#git) | `2.31.x` | From GitLab 13.11, Git 2.31.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). |
|
||||
| [Node.js](#4-node) | `12.22.1` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 14.x is recommended, as it's faster. You can check which version you're running with `node -v`. You need to update it to a newer version if needed. |
|
||||
|
||||
## GitLab directory structure
|
||||
|
||||
This is the main directory structure you end up with following the instructions
|
||||
|
@ -207,7 +216,7 @@ sudo apt-get install -y libimage-exiftool-perl
|
|||
## 2. Ruby
|
||||
|
||||
The Ruby interpreter is required to run GitLab.
|
||||
See the [requirements page](requirements.md#ruby-versions) for the minimum
|
||||
See the [requirements section of this page](#software-requirements) for the minimum
|
||||
Ruby requirements.
|
||||
|
||||
The use of Ruby version managers such as [`RVM`](https://rvm.io/), [`rbenv`](https://github.com/rbenv/rbenv) or [`chruby`](https://github.com/postmodern/chruby) with GitLab
|
||||
|
|
|
@ -47,55 +47,6 @@ Please consider using a virtual machine to run GitLab.
|
|||
|
||||
## Software requirements
|
||||
|
||||
### Ruby versions
|
||||
|
||||
From GitLab 13.6:
|
||||
|
||||
- Ruby 2.7 and later is required.
|
||||
|
||||
From GitLab 12.2:
|
||||
|
||||
- Ruby 2.6 and later is required.
|
||||
|
||||
You must use the standard MRI implementation of Ruby.
|
||||
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab
|
||||
needs several Gems that have native extensions.
|
||||
|
||||
### Go versions
|
||||
|
||||
The minimum required Go version is 1.13.
|
||||
|
||||
### Git versions
|
||||
|
||||
From GitLab 13.11:
|
||||
|
||||
- Git 2.31.x and later is required. We recommend you use the
|
||||
[Git version provided by Gitaly](installation.md#git).
|
||||
|
||||
From GitLab 13.6:
|
||||
|
||||
- Git 2.29.x and later is required.
|
||||
|
||||
From GitLab 13.1:
|
||||
|
||||
- Git 2.24.x and later is required.
|
||||
- Git 2.28.x and later [is recommended](https://gitlab.com/gitlab-org/gitaly/-/issues/2959).
|
||||
|
||||
### Node.js versions
|
||||
|
||||
Beginning in GitLab 12.9, we only support Node.js 10.13.0 or higher, and we have dropped
|
||||
support for Node.js 8. (Node.js 6 support was dropped in GitLab 11.8)
|
||||
|
||||
We recommend Node 14.x, as it's faster.
|
||||
|
||||
GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets, which requires a minimum
|
||||
version of Node.js 10.13.0.
|
||||
|
||||
You can check which version you're running with `node -v`. If you're running
|
||||
a version older than `v10.13.0`, you need to update it to a newer version. You
|
||||
can find instructions to install from community maintained packages or compile
|
||||
from source at the [Node.js website](https://nodejs.org/en/download/).
|
||||
|
||||
### Redis versions
|
||||
|
||||
GitLab 13.0 and later requires Redis version 4.0 or higher.
|
||||
|
|
|
@ -82,7 +82,7 @@ sudo make install
|
|||
|
||||
### 4. Update Node.js
|
||||
|
||||
To check the minimum required Node.js version, see [Node.js versions](../install/requirements.md#nodejs-versions).
|
||||
To check the minimum required Node.js version, see [Node.js versions](../install/installation.md#software-requirements).
|
||||
|
||||
GitLab also requires the use of Yarn `>= v1.10.0` to manage JavaScript
|
||||
dependencies.
|
||||
|
@ -99,7 +99,7 @@ More information can be found on the [Yarn website](https://classic.yarnpkg.com/
|
|||
|
||||
### 5. Update Go
|
||||
|
||||
To check the minimum required Go version, see [Go versions](../install/requirements.md#go-versions).
|
||||
To check the minimum required Go version, see [Go versions](../install/installation.md#software-requirements).
|
||||
|
||||
You can check which version you are running with `go version`.
|
||||
|
||||
|
@ -119,12 +119,8 @@ rm go1.13.5.linux-amd64.tar.gz
|
|||
|
||||
### 6. Update Git
|
||||
|
||||
WARNING:
|
||||
From GitLab 13.1, you must use at least Git v2.24 (previous minimum version was v2.22).
|
||||
Git v2.28 is recommended.
|
||||
|
||||
To check you are running the minimum required Git version, see
|
||||
[Git versions](../install/requirements.md#git-versions).
|
||||
[Git versions](../install/installation.md#software-requirements).
|
||||
|
||||
From GitLab 13.6, we recommend you use the [Git version provided by
|
||||
Gitaly](https://gitlab.com/gitlab-org/gitaly/-/issues/2729)
|
||||
|
|
|
@ -12,8 +12,7 @@ module Gitlab
|
|||
|
||||
DEFAULT_VALIDATION_REQUEST_TIMEOUT = 5
|
||||
ACCEPTED_STATUS = 200
|
||||
DOT_COM_REJECTED_STATUS = 406
|
||||
GENERAL_REJECTED_STATUS = (400..499).freeze
|
||||
REJECTED_STATUS = 406
|
||||
|
||||
def perform!
|
||||
pipeline_authorized = validate_external
|
||||
|
@ -34,14 +33,13 @@ module Gitlab
|
|||
return true unless validation_service_url
|
||||
|
||||
# 200 - accepted
|
||||
# 406 - not accepted on GitLab.com
|
||||
# 4XX - not accepted for other installations
|
||||
# 406 - rejected
|
||||
# everything else - accepted and logged
|
||||
response_code = validate_service_request.code
|
||||
case response_code
|
||||
when ACCEPTED_STATUS
|
||||
true
|
||||
when rejected_status
|
||||
when REJECTED_STATUS
|
||||
false
|
||||
else
|
||||
raise InvalidResponseCode, "Unsupported response code received from Validation Service: #{response_code}"
|
||||
|
@ -52,14 +50,6 @@ module Gitlab
|
|||
true
|
||||
end
|
||||
|
||||
def rejected_status
|
||||
if Gitlab.com?
|
||||
DOT_COM_REJECTED_STATUS
|
||||
else
|
||||
GENERAL_REJECTED_STATUS
|
||||
end
|
||||
end
|
||||
|
||||
def validate_service_request
|
||||
headers = {
|
||||
'X-Gitlab-Correlation-id' => Labkit::Correlation::CorrelationId.current_id,
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Cluster
|
||||
module Mixins
|
||||
module UnicornHttpServer
|
||||
def self.prepended(base)
|
||||
unless base.method_defined?(:reexec) && base.method_defined?(:stop)
|
||||
raise 'missing method Unicorn::HttpServer#reexec or Unicorn::HttpServer#stop'
|
||||
end
|
||||
end
|
||||
|
||||
def reexec
|
||||
Gitlab::Cluster::LifecycleEvents.do_before_graceful_shutdown
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
# The stop on non-graceful shutdown is executed twice:
|
||||
# `#stop(false)` and `#stop`.
|
||||
#
|
||||
# The first stop will wipe-out all workers, so we need to check
|
||||
# the flag and a list of workers
|
||||
def stop(graceful = true)
|
||||
if graceful && @workers.any? # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
Gitlab::Cluster::LifecycleEvents.do_before_graceful_shutdown
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module HealthChecks
|
||||
# This check can only be run on Unicorn `master` process
|
||||
class UnicornCheck
|
||||
extend SimpleAbstractCheck
|
||||
|
||||
class << self
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
'unicorn_check'
|
||||
end
|
||||
|
||||
def successful?(result)
|
||||
result > 0
|
||||
end
|
||||
|
||||
def check
|
||||
return unless http_servers
|
||||
|
||||
http_servers.sum(&:worker_processes)
|
||||
end
|
||||
|
||||
# Traversal of ObjectSpace is expensive, on fully loaded application
|
||||
# it takes around 80ms. The instances of HttpServers are not a subject
|
||||
# to change so we can cache the list of servers.
|
||||
def http_servers
|
||||
strong_memoize(:http_servers) do
|
||||
next unless Gitlab::Runtime.unicorn?
|
||||
|
||||
ObjectSpace.each_object(::Unicorn::HttpServer).to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,8 +30,7 @@ module Gitlab
|
|||
# application: https://gitlab.com/gitlab-org/gitlab/issues/35343
|
||||
self.readiness_checks = [
|
||||
WebExporter::ExporterCheck.new(self),
|
||||
Gitlab::HealthChecks::PumaCheck,
|
||||
Gitlab::HealthChecks::UnicornCheck
|
||||
Gitlab::HealthChecks::PumaCheck
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.197.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "29.27.0",
|
||||
"@gitlab/ui": "29.28.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
|
|
|
@ -16,7 +16,7 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it 'creates a basic merge request', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1276' do
|
||||
it 'creates a basic merge request', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1850' do
|
||||
Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request|
|
||||
merge_request.project = project
|
||||
merge_request.title = merge_request_title
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Git push over HTTP', :smoke do
|
||||
it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1278' do
|
||||
it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1848' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
access_token = Resource::PersonalAccessToken.fabricate!.token
|
||||
|
|
|
@ -24,7 +24,7 @@ module QA
|
|||
runner.remove_via_api!
|
||||
end
|
||||
|
||||
it 'users creates a pipeline which gets processed', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1279' do
|
||||
it 'users creates a pipeline which gets processed', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1849' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
|
|
|
@ -115,7 +115,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'runs an AutoDevOps pipeline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1277' do
|
||||
it 'runs an AutoDevOps pipeline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1847' do
|
||||
Flow::Pipeline.visit_latest_pipeline
|
||||
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module UsageData
|
||||
# This cop checks that metric instrumentation classes subclass one of the allowed base classes.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # good
|
||||
# class CountIssues < DatabaseMetric
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# # bad
|
||||
# class CountIssues < BaseMetric
|
||||
# # ...
|
||||
# end
|
||||
class InstrumentationSuperclass < RuboCop::Cop::Cop
|
||||
MSG = "Instrumentation classes should subclass one of the following: %{allowed_classes}."
|
||||
|
||||
BASE_PATTERN = "(const nil? !#allowed_class?)"
|
||||
|
||||
def_node_matcher :class_definition, <<~PATTERN
|
||||
(class (const _ !#allowed_class?) #{BASE_PATTERN} ...)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :class_new_definition, <<~PATTERN
|
||||
[!^(casgn {nil? cbase} #allowed_class? ...)
|
||||
!^^(casgn {nil? cbase} #allowed_class? (block ...))
|
||||
(send (const {nil? cbase} :Class) :new #{BASE_PATTERN})]
|
||||
PATTERN
|
||||
|
||||
def on_class(node)
|
||||
class_definition(node) do
|
||||
register_offense(node.children[1])
|
||||
end
|
||||
end
|
||||
|
||||
def on_send(node)
|
||||
class_new_definition(node) do
|
||||
register_offense(node.children.last)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_class?(class_name)
|
||||
allowed_classes.include?(class_name)
|
||||
end
|
||||
|
||||
def allowed_classes
|
||||
cop_config['AllowedClasses'] || []
|
||||
end
|
||||
|
||||
def register_offense(offense_node)
|
||||
message = format(MSG, allowed_classes: allowed_classes.join(', '))
|
||||
add_offense(offense_node, message: message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -57,3 +57,11 @@ UsageData/DistinctCountByLargeForeignKey:
|
|||
- 'user_id'
|
||||
- 'resource_owner_id'
|
||||
- 'requirement_id'
|
||||
UsageData/InstrumentationSuperclass:
|
||||
Enabled: true
|
||||
Include:
|
||||
- 'lib/gitlab/usage/metrics/instrumentations/**/*.rb'
|
||||
AllowedClasses:
|
||||
- :DatabaseMetric
|
||||
- :GenericMetric
|
||||
- :RedisHLLMetric
|
||||
|
|
|
@ -16,9 +16,7 @@ RSpec.describe 'Issues csv', :js do
|
|||
|
||||
def request_csv(params = {})
|
||||
visit project_issues_path(project, params)
|
||||
page.within('.nav-controls') do
|
||||
find('[data-testid="export-csv-button"]').click
|
||||
end
|
||||
click_button 'Export as CSV'
|
||||
click_on 'Export issues'
|
||||
end
|
||||
|
||||
|
|
|
@ -16,11 +16,11 @@ RSpec.describe 'Issues > User resets their incoming email token' do
|
|||
end
|
||||
|
||||
it 'changes incoming email address token', :js do
|
||||
page.find('[data-testid="issuable-email-modal-btn"]').click
|
||||
click_button 'Email a new issue to this project'
|
||||
|
||||
page.within '#issuable-email-modal' do
|
||||
previous_token = page.find('input[type="text"]').value
|
||||
page.find('[data-testid="incoming-email-token-reset"]').click
|
||||
click_button 'reset it'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
|
|
|
@ -12,15 +12,9 @@ RSpec.describe 'Merge Requests > Exports as CSV', :js do
|
|||
visit(project_merge_requests_path(project))
|
||||
end
|
||||
|
||||
subject { page.find('.nav-controls') }
|
||||
|
||||
it { is_expected.to have_selector '[data-testid="export-csv-button"]' }
|
||||
|
||||
context 'button is clicked' do
|
||||
before do
|
||||
page.within('.nav-controls') do
|
||||
find('[data-testid="export-csv-button"]').click
|
||||
end
|
||||
click_button 'Export as CSV'
|
||||
end
|
||||
|
||||
it 'shows a success message' do
|
||||
|
|
|
@ -116,7 +116,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
|
|||
click_on('Save changes')
|
||||
end
|
||||
|
||||
find('.flash-notice')
|
||||
wait_for_all_requests
|
||||
|
||||
checkbox = find_field('project_printing_merge_request_link_enabled')
|
||||
|
||||
expect(checkbox).not_to be_checked
|
||||
|
@ -139,7 +140,8 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
|
|||
click_on('Save changes')
|
||||
end
|
||||
|
||||
find('.flash-notice')
|
||||
wait_for_all_requests
|
||||
|
||||
checkbox = find_field('project_remove_source_branch_after_merge')
|
||||
|
||||
expect(checkbox).not_to be_checked
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { GlModal, GlIcon, GlButton } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import CsvExportModal from '~/issuable/components/csv_export_modal.vue';
|
||||
|
||||
describe('CsvExportModal', () => {
|
||||
|
@ -9,8 +8,7 @@ describe('CsvExportModal', () => {
|
|||
|
||||
function createComponent(options = {}) {
|
||||
const { injectedProperties = {}, props = {} } = options;
|
||||
return extendedWrapper(
|
||||
mount(CsvExportModal, {
|
||||
return mount(CsvExportModal, {
|
||||
propsData: {
|
||||
modalId: 'csv-export-modal',
|
||||
exportCsvPath: 'export/csv/path',
|
||||
|
@ -27,8 +25,7 @@ describe('CsvExportModal', () => {
|
|||
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -61,14 +58,13 @@ describe('CsvExportModal', () => {
|
|||
describe('issuable count info text', () => {
|
||||
it('displays the info text when issuableCount is > -1', () => {
|
||||
wrapper = createComponent({ props: { issuableCount: 10 } });
|
||||
expect(wrapper.findByTestId('issuable-count-note').exists()).toBe(true);
|
||||
expect(wrapper.findByTestId('issuable-count-note').text()).toContain('10 issues selected');
|
||||
expect(wrapper.text()).toContain('10 issues selected');
|
||||
expect(findIcon().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("doesn't display the info text when issuableCount is -1", () => {
|
||||
wrapper = createComponent({ props: { issuableCount: -1 } });
|
||||
expect(wrapper.findByTestId('issuable-count-note').exists()).toBe(false);
|
||||
expect(wrapper.text()).not.toContain('issues selected');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlButton, GlDropdown } from '@gitlab/ui';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import CsvExportModal from '~/issuable/components/csv_export_modal.vue';
|
||||
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
|
||||
import CsvImportModal from '~/issuable/components/csv_import_modal.vue';
|
||||
|
@ -14,8 +14,7 @@ describe('CsvImportExportButtons', () => {
|
|||
|
||||
function createComponent(injectedProperties = {}) {
|
||||
glModalDirective = jest.fn();
|
||||
return extendedWrapper(
|
||||
shallowMount(CsvImportExportButtons, {
|
||||
return mountExtended(CsvImportExportButtons, {
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
glModal: {
|
||||
|
@ -31,18 +30,17 @@ describe('CsvImportExportButtons', () => {
|
|||
exportCsvPath,
|
||||
issuableCount,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findExportCsvButton = () => wrapper.findByTestId('export-csv-button');
|
||||
const findImportDropdown = () => wrapper.findByTestId('import-csv-dropdown');
|
||||
const findImportCsvButton = () => wrapper.findByTestId('import-csv-dropdown');
|
||||
const findImportFromJiraLink = () => wrapper.findByTestId('import-from-jira-link');
|
||||
const findExportCsvButton = () => wrapper.findComponent(GlButton);
|
||||
const findImportDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findImportCsvButton = () => wrapper.findByRole('menuitem', { name: 'Import CSV' });
|
||||
const findImportFromJiraLink = () => wrapper.findByRole('menuitem', { name: 'Import from Jira' });
|
||||
const findExportCsvModal = () => wrapper.findComponent(CsvExportModal);
|
||||
const findImportCsvModal = () => wrapper.findComponent(CsvImportModal);
|
||||
|
||||
|
@ -97,7 +95,7 @@ describe('CsvImportExportButtons', () => {
|
|||
expect(findImportDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the import button', () => {
|
||||
it('renders the import csv menu item', () => {
|
||||
expect(findImportCsvButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -106,8 +104,11 @@ describe('CsvImportExportButtons', () => {
|
|||
wrapper = createComponent({ showImportButton: true, showLabel: false });
|
||||
});
|
||||
|
||||
it('does not have a button text', () => {
|
||||
expect(findImportCsvButton().props('text')).toBe(null);
|
||||
it('hides button text', () => {
|
||||
expect(findImportDropdown().props()).toMatchObject({
|
||||
text: 'Import issues',
|
||||
textSrOnly: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('import button has a tooltip', () => {
|
||||
|
@ -124,7 +125,10 @@ describe('CsvImportExportButtons', () => {
|
|||
});
|
||||
|
||||
it('displays a button text', () => {
|
||||
expect(findImportCsvButton().props('text')).toBe('Import issues');
|
||||
expect(findImportDropdown().props()).toMatchObject({
|
||||
text: 'Import issues',
|
||||
textSrOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('import button has no tooltip', () => {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { getByRole, getByLabelText } from '@testing-library/dom';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { GlButton, GlModal } from '@gitlab/ui';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import CsvImportModal from '~/issuable/components/csv_import_modal.vue';
|
||||
|
||||
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
|
||||
|
@ -13,8 +11,7 @@ describe('CsvImportModal', () => {
|
|||
|
||||
function createComponent(options = {}) {
|
||||
const { injectedProperties = {}, props = {} } = options;
|
||||
return extendedWrapper(
|
||||
mount(CsvImportModal, {
|
||||
return mountExtended(CsvImportModal, {
|
||||
propsData: {
|
||||
modalId: 'csv-import-modal',
|
||||
...props,
|
||||
|
@ -28,8 +25,7 @@ describe('CsvImportModal', () => {
|
|||
template: '<div><slot></slot><slot name="modal-footer"></slot></div>',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -41,9 +37,9 @@ describe('CsvImportModal', () => {
|
|||
});
|
||||
|
||||
const findModal = () => wrapper.findComponent(GlModal);
|
||||
const findPrimaryButton = () => getByRole(wrapper.element, 'button', { name: 'Import issues' });
|
||||
const findForm = () => wrapper.findByTestId('import-csv-form');
|
||||
const findFileInput = () => getByLabelText(wrapper.element, 'Upload CSV file');
|
||||
const findPrimaryButton = () => wrapper.findComponent(GlButton);
|
||||
const findForm = () => wrapper.find('form');
|
||||
const findFileInput = () => wrapper.findByLabelText('Upload CSV file');
|
||||
const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token');
|
||||
|
||||
describe('template', () => {
|
||||
|
@ -76,8 +72,8 @@ describe('CsvImportModal', () => {
|
|||
expect(findPrimaryButton()).toExist();
|
||||
});
|
||||
|
||||
it('submits the form when the primary action is clicked', async () => {
|
||||
findPrimaryButton().click();
|
||||
it('submits the form when the primary action is clicked', () => {
|
||||
findPrimaryButton().trigger('click');
|
||||
|
||||
expect(formSubmitSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
@ -58,10 +58,11 @@ describe('IssuableByEmail', () => {
|
|||
mockAxios.restore();
|
||||
});
|
||||
|
||||
const findFormInputGroup = () => wrapper.find(GlFormInputGroup);
|
||||
const findButton = () => wrapper.findComponent(GlButton);
|
||||
const findFormInputGroup = () => wrapper.findComponent(GlFormInputGroup);
|
||||
|
||||
const clickResetEmail = async () => {
|
||||
wrapper.findByTestId('incoming-email-token-reset').vm.$emit('click');
|
||||
wrapper.findAllComponents(GlButton).at(2).trigger('click');
|
||||
|
||||
await waitForPromises();
|
||||
};
|
||||
|
@ -75,14 +76,14 @@ describe('IssuableByEmail', () => {
|
|||
'renders a link with "$buttonText" when type is "$issuableType"',
|
||||
({ issuableType, buttonText }) => {
|
||||
wrapper = createComponent({ issuableType });
|
||||
expect(wrapper.findByTestId('issuable-email-modal-btn').text()).toBe(buttonText);
|
||||
expect(findButton().text()).toBe(buttonText);
|
||||
},
|
||||
);
|
||||
|
||||
it('opens the modal when the user clicks the button', () => {
|
||||
wrapper = createComponent();
|
||||
|
||||
wrapper.findByTestId('issuable-email-modal-btn').vm.$emit('click');
|
||||
findButton().trigger('click');
|
||||
|
||||
expect(glModalDirective).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -105,7 +106,7 @@ describe('IssuableByEmail', () => {
|
|||
initialEmail,
|
||||
});
|
||||
|
||||
expect(wrapper.findByTestId('mail-to-btn').attributes('href')).toBe(
|
||||
expect(wrapper.findAllComponents(GlButton).at(1).attributes('href')).toBe(
|
||||
`mailto:${initialEmail}?subject=${subject}&body=${body}`,
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { GlIcon, GlSprintf } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import StatusBox from '~/issuable/components/status_box.vue';
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe('Merge request status box component', () => {
|
|||
initialState: testCase.state,
|
||||
});
|
||||
|
||||
expect(wrapper.find('[data-testid="status-icon"]').props('name')).toBe(testCase.icon);
|
||||
expect(wrapper.findComponent(GlIcon).props('name')).toBe(testCase.icon);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -798,15 +798,29 @@ describe('URL utility', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('handles arrays properly', () => {
|
||||
it('adds parameters from arrays', () => {
|
||||
const url = 'https://gitlab.com/test';
|
||||
|
||||
expect(urlUtils.setUrlParams({ label_name: ['foo', 'bar'] }, url)).toEqual(
|
||||
'https://gitlab.com/test?label_name=foo&label_name=bar',
|
||||
expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url)).toEqual(
|
||||
'https://gitlab.com/test?labels=foo&labels=bar',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles arrays properly when railsArraySyntax=true', () => {
|
||||
it('removes parameters from empty arrays', () => {
|
||||
const url = 'https://gitlab.com/test?labels=foo&labels=bar';
|
||||
|
||||
expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual('https://gitlab.com/test');
|
||||
});
|
||||
|
||||
it('removes parameters from empty arrays while keeping other parameters', () => {
|
||||
const url = 'https://gitlab.com/test?labels=foo&labels=bar&unrelated=unrelated';
|
||||
|
||||
expect(urlUtils.setUrlParams({ labels: [] }, url)).toEqual(
|
||||
'https://gitlab.com/test?unrelated=unrelated',
|
||||
);
|
||||
});
|
||||
|
||||
it('adds parameters from arrays when railsArraySyntax=true', () => {
|
||||
const url = 'https://gitlab.com/test';
|
||||
|
||||
expect(urlUtils.setUrlParams({ labels: ['foo', 'bar'] }, url, false, true)).toEqual(
|
||||
|
@ -814,6 +828,14 @@ describe('URL utility', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('removes parameters from empty arrays when railsArraySyntax=true', () => {
|
||||
const url = 'https://gitlab.com/test?labels%5B%5D=foo&labels%5B%5D=bar';
|
||||
|
||||
expect(urlUtils.setUrlParams({ labels: [] }, url, false, true)).toEqual(
|
||||
'https://gitlab.com/test',
|
||||
);
|
||||
});
|
||||
|
||||
it('decodes URI when decodeURI=true', () => {
|
||||
const url = 'https://gitlab.com/test';
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
|
|||
end
|
||||
|
||||
let(:save_incompleted) { true }
|
||||
let(:dot_com) { true }
|
||||
let(:command) do
|
||||
Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||
project: project, current_user: user, yaml_processor_result: yaml_processor_result, save_incompleted: save_incompleted
|
||||
|
@ -57,7 +56,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
|
|||
|
||||
before do
|
||||
stub_env('EXTERNAL_VALIDATION_SERVICE_URL', validation_service_url)
|
||||
allow(Gitlab).to receive(:com?).and_return(dot_com)
|
||||
allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('correlation-id')
|
||||
end
|
||||
|
||||
|
@ -199,34 +197,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when not on .com' do
|
||||
let(:dot_com) { false }
|
||||
|
||||
before do
|
||||
stub_request(:post, validation_service_url).to_return(status: 404, body: "{}")
|
||||
end
|
||||
|
||||
it 'drops the pipeline' do
|
||||
perform!
|
||||
|
||||
expect(pipeline.status).to eq('failed')
|
||||
expect(pipeline).to be_persisted
|
||||
expect(pipeline.errors.to_a).to include('External validation failed')
|
||||
end
|
||||
|
||||
it 'breaks the chain' do
|
||||
perform!
|
||||
|
||||
expect(step.break?).to be true
|
||||
end
|
||||
|
||||
it 'logs the authorization' do
|
||||
expect(Gitlab::AppLogger).to receive(:info).with(message: 'Pipeline not authorized', project_id: project.id, user_id: user.id)
|
||||
|
||||
perform!
|
||||
end
|
||||
end
|
||||
|
||||
context 'when validation returns 406 Not Acceptable' do
|
||||
before do
|
||||
stub_request(:post, validation_service_url).to_return(status: 406, body: "{}")
|
||||
|
|
|
@ -1,117 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
# For easier debugging set `UNICORN_DEBUG=1`
|
||||
|
||||
RSpec.describe Gitlab::Cluster::Mixins::UnicornHttpServer do
|
||||
before do
|
||||
stub_const('UNICORN_STARTUP_TIMEOUT', 30)
|
||||
end
|
||||
|
||||
context 'when running Unicorn' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:signal, :exitstatus, :termsig) do
|
||||
# executes phased restart block
|
||||
:USR2 | 140 | nil
|
||||
:QUIT | 140 | nil
|
||||
|
||||
# does not execute phased restart block
|
||||
:INT | 0 | nil
|
||||
:TERM | 0 | nil
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'properly handles process lifecycle' do
|
||||
with_unicorn(workers: 1) do |pid|
|
||||
Process.kill(signal, pid)
|
||||
|
||||
child_pid, child_status = Process.wait2(pid)
|
||||
expect(child_pid).to eq(pid)
|
||||
expect(child_status.exitstatus).to eq(exitstatus)
|
||||
expect(child_status.termsig).to eq(termsig)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_unicorn(workers:, timeout: UNICORN_STARTUP_TIMEOUT)
|
||||
with_unicorn_configs(workers: workers) do |unicorn_rb, config_ru|
|
||||
cmdline = [
|
||||
"bundle", "exec", "unicorn",
|
||||
"-I", Rails.root.to_s,
|
||||
"-c", unicorn_rb,
|
||||
config_ru
|
||||
]
|
||||
|
||||
IO.popen(cmdline) do |process|
|
||||
# wait for process to start:
|
||||
# I, [2019-10-15T13:21:27.565225 #3089] INFO -- : master process ready
|
||||
wait_for_output(process, /master process ready/, timeout: timeout)
|
||||
consume_output(process)
|
||||
|
||||
yield(process.pid)
|
||||
ensure
|
||||
begin
|
||||
Process.kill(:KILL, process.pid)
|
||||
rescue Errno::ESRCH
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_unicorn_configs(workers:)
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write "#{dir}/unicorn.rb", <<-EOF
|
||||
require './lib/gitlab/cluster/lifecycle_events'
|
||||
require './lib/gitlab/cluster/mixins/unicorn_http_server'
|
||||
|
||||
worker_processes #{workers}
|
||||
listen "127.0.0.1:0"
|
||||
preload_app true
|
||||
|
||||
Unicorn::HttpServer.prepend(#{described_class})
|
||||
|
||||
mutex = Mutex.new
|
||||
|
||||
Gitlab::Cluster::LifecycleEvents.on_before_blackout_period do
|
||||
mutex.synchronize do
|
||||
exit(140)
|
||||
end
|
||||
end
|
||||
|
||||
# redirect stderr to stdout
|
||||
$stderr.reopen($stdout)
|
||||
EOF
|
||||
|
||||
File.write "#{dir}/config.ru", <<-EOF
|
||||
run -> (env) { [404, {}, ['']] }
|
||||
EOF
|
||||
|
||||
yield("#{dir}/unicorn.rb", "#{dir}/config.ru")
|
||||
end
|
||||
end
|
||||
|
||||
def wait_for_output(process, output, timeout:)
|
||||
Timeout.timeout(timeout) do
|
||||
loop do
|
||||
line = process.readline
|
||||
puts "UNICORN_DEBUG: #{line}" if ENV['UNICORN_DEBUG']
|
||||
break if line =~ output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def consume_output(process)
|
||||
Thread.new do
|
||||
loop do
|
||||
line = process.readline
|
||||
puts "UNICORN_DEBUG: #{line}" if ENV['UNICORN_DEBUG']
|
||||
end
|
||||
rescue StandardError
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::HealthChecks::UnicornCheck do
|
||||
let(:result_class) { Gitlab::HealthChecks::Result }
|
||||
let(:readiness) { described_class.readiness }
|
||||
let(:metrics) { described_class.metrics }
|
||||
|
||||
before do
|
||||
described_class.clear_memoization(:http_servers)
|
||||
end
|
||||
|
||||
shared_examples 'with state' do |(state, message)|
|
||||
it "does provide readiness" do
|
||||
expect(readiness).to eq(result_class.new('unicorn_check', state, message))
|
||||
end
|
||||
|
||||
it "does provide metrics" do
|
||||
expect(metrics).to include(
|
||||
an_object_having_attributes(name: 'unicorn_check_success', value: state ? 1 : 0))
|
||||
expect(metrics).to include(
|
||||
an_object_having_attributes(name: 'unicorn_check_latency_seconds', value: be >= 0))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Unicorn is not loaded' do
|
||||
before do
|
||||
allow(Gitlab::Runtime).to receive(:unicorn?).and_return(false)
|
||||
hide_const('Unicorn')
|
||||
end
|
||||
|
||||
it "does not provide readiness and metrics" do
|
||||
expect(readiness).to be_nil
|
||||
expect(metrics).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Unicorn is loaded' do
|
||||
let(:http_server_class) { Struct.new(:worker_processes) }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
|
||||
stub_const('Unicorn::HttpServer', http_server_class)
|
||||
end
|
||||
|
||||
context 'when no servers are running' do
|
||||
it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0']
|
||||
end
|
||||
|
||||
context 'when servers without workers are running' do
|
||||
before do
|
||||
http_server_class.new(0)
|
||||
end
|
||||
|
||||
it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0']
|
||||
end
|
||||
|
||||
context 'when servers with workers are running' do
|
||||
before do
|
||||
http_server_class.new(1)
|
||||
end
|
||||
|
||||
it_behaves_like 'with state', true
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
|
||||
require_relative '../../../../rubocop/cop/usage_data/instrumentation_superclass'
|
||||
|
||||
RSpec.describe RuboCop::Cop::UsageData::InstrumentationSuperclass do
|
||||
let(:allowed_classes) { %i[GenericMetric DatabaseMetric RedisHllMetric] }
|
||||
let(:msg) { "Instrumentation classes should subclass one of the following: #{allowed_classes.join(', ')}." }
|
||||
|
||||
let(:config) do
|
||||
RuboCop::Config.new('UsageData/InstrumentationSuperclass' => {
|
||||
'AllowedClasses' => allowed_classes
|
||||
})
|
||||
end
|
||||
|
||||
subject(:cop) { described_class.new(config) }
|
||||
|
||||
context 'with class definition' do
|
||||
context 'when inheriting from allowed superclass' do
|
||||
it 'does not register an offense' do
|
||||
expect_no_offenses('class NewMetric < GenericMetric; end')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inheriting from some other superclass' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
class NewMetric < BaseMetric; end
|
||||
^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not inheriting' do
|
||||
it 'does not register an offense' do
|
||||
expect_no_offenses('class NewMetric; end')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with dynamic class definition' do
|
||||
context 'when inheriting from allowed superclass' do
|
||||
it 'does not register an offense' do
|
||||
expect_no_offenses('NewMetric = Class.new(GenericMetric)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when inheriting from some other superclass' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
NewMetric = Class.new(BaseMetric)
|
||||
^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not inheriting' do
|
||||
it 'does not register an offense' do
|
||||
expect_no_offenses('NewMetric = Class.new')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -908,10 +908,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@29.27.0":
|
||||
version "29.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.27.0.tgz#c5cbe1fee2164705ea6a431c85f6fdaa2ff7e166"
|
||||
integrity sha512-VoiYrozWyE9l/ddX308vsu+wQqaJJN3csngIlrJit3DzVZV9Z/OVWU/X9CWV8m/ubyr5ysEMqnrtnsQClR9FiA==
|
||||
"@gitlab/ui@29.28.0":
|
||||
version "29.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.28.0.tgz#3444f6d26114f503d78b85fca67b5cc340a4a667"
|
||||
integrity sha512-7jHqbnEy3P5J/G0/b+Nu+iw8XSOyTWLvyOEtNdFpBras1RxCE3C4AnPyGT7jei+WafTQN2Vzxz8VIgAxZ6PPvg==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in New Issue