Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-10 15:11:19 +00:00
parent ec0ecba05c
commit 6cffe9ea21
60 changed files with 773 additions and 160 deletions

View File

@ -61,7 +61,7 @@ Consider adding checkboxes and expectations of users with certain levels of memb
<!-- See the Feature Change Documentation Workflow https://docs.gitlab.com/ee/development/documentation/workflow.html#for-a-product-change
* Add all known Documentation Requirements in this section. See https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements
* Add all known Documentation Requirements in this section. See https://docs.gitlab.com/ee/development/documentation/workflow.html
* If this feature requires changing permissions, update the permissions document. See https://docs.gitlab.com/ee/user/permissions.html -->
### Availability & Testing

View File

@ -73,7 +73,7 @@ Consider adding checkboxes and expectations of users with certain levels of memb
See the Feature Change Documentation Workflow https://docs.gitlab.com/ee/development/documentation/workflow.html#for-a-product-change
* Add all known Documentation Requirements in this section. See https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements
* Add all known Documentation Requirements in this section. See https://docs.gitlab.com/ee/development/documentation/workflow.html
* If this feature requires changing permissions, update the permissions document. See https://docs.gitlab.com/ee/user/permissions.html
### Availability & Testing

View File

@ -1 +1 @@
320f41869d84d64ebbec8d3e0febfb37eca05ffb
88ef3e7f64498ae3574f29b0705c29cf3b4e9311

View File

@ -15,6 +15,7 @@ import {
GlPagination,
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { helpPagePath } from '~/helpers/help_page_helper';
import AccessorUtils from '~/lib/utils/accessor';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
@ -138,6 +139,9 @@ export default {
paginationRequired() {
return !isEmpty(this.pagination);
},
errorTrackingHelpUrl() {
return helpPagePath('operations/error_tracking');
},
},
watch: {
pagination() {
@ -404,7 +408,7 @@ export default {
<template #description>
<div>
<span>{{ __('Monitor your errors by integrating with Sentry.') }}</span>
<gl-link target="_blank" href="/help/user/project/operations/error_tracking.html">{{
<gl-link target="_blank" :href="errorTrackingHelpUrl">{{
__('More information')
}}</gl-link>
</div>

View File

@ -159,7 +159,10 @@ class JiraService < IssueTrackerService
# support any events.
end
def find_issue(issue_key, options = {})
def find_issue(issue_key, rendered_fields: false)
options = {}
options = options.merge(expand: 'renderedFields') if rendered_fields
jira_request { client.Issue.find(issue_key, options) }
end

View File

@ -13,7 +13,7 @@
%button.gl-alert-dismiss.js-close-2fa-enabled-success-alert{ type: 'button', aria: { label: _('Close') } }
= sprite_icon('close', size: 16)
.gl-alert-body
= _('Congratulations! You have enabled Two-factor Authentication!')
= html_escape(_('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can %{anchorOpen}use that key to generate additional recovery codes%{anchorClose}.')) % { anchorOpen: '<a href="%{href}">'.html_safe % { href: help_page_path('user/profile/account/two_factor_authentication', anchor: 'generate-new-recovery-codes-using-ssh') }, anchorClose: '</a>'.html_safe }
.row.gl-mt-3.js-search-settings-section
.col-lg-4.profile-settings-sidebar

View File

@ -26,8 +26,7 @@
- else
%p
- help_link_start = '<a href="%{url}" target="_blank">' % { url: help_page_path('user/profile/account/two_factor_authentication') }
- register_2fa_token = _('Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}.') % { free_otp_link:'<a href="https://freeotp.github.io/">FreeOTP</a>', help_link_start:help_link_start, help_link_end:'</a>' }
- register_2fa_token = _('We recommend cloud-based mobile authenticator apps such as Authy, Duo Mobile, and LastPass. They can restore access if you lose your hardware device.')
= register_2fa_token.html_safe
.row.gl-mb-3
.col-md-4

View File

@ -0,0 +1,5 @@
---
title: Change UI text for 2FA setup
merge_request: 53677
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Update links to redirected docs
merge_request: 53004
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Map common severity values from a Prometheus alert payload
merge_request: 50871
author:
type: added

View File

@ -0,0 +1,8 @@
---
name: rubygem_packages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52147
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299383
milestone: '13.9'
type: development
group: group::package
default_enabled: false

View File

@ -1,8 +1,8 @@
---
name: user_mode_in_session
introduced_by_url:
rollout_issue_url:
milestone:
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16981
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321025
milestone: 12.4
type: development
group:
group: group::access
default_enabled: false

View File

@ -416,7 +416,7 @@ production: &base
## Gravatar
## If using gravatar.com, there's nothing to change here. For Libravatar
## you'll need to provide the custom URLs. For more information,
## see: https://docs.gitlab.com/ee/customization/libravatar.html
## see: https://docs.gitlab.com/ee/administration/libravatar.html
gravatar:
# Gravatar/Libravatar URLs: possible placeholders: %{hash} %{size} %{email} %{username}
# plain_url: "http://..." # default: https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
@ -590,7 +590,7 @@ production: &base
# enabled: true
# primary_api_url: http://localhost:5000/ # internal address to the primary registry, will be used by GitLab to directly communicate with primary registry API
## Feature Flag https://docs.gitlab.com/ee/user/project/operations/feature_flags.html
## Feature Flag https://docs.gitlab.com/ee/operations/feature_flags.html
feature_flags:
unleash:
# enabled: false

View File

@ -3,7 +3,7 @@
CHANGED_FILES_MESSAGE = <<~MSG
For the following files, a review from the [Data team and Product Intelligence team](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) is recommended
Please check the ~"product intelligence" [guide](https://docs.gitlab.com/ee/development/product_analytics/usage_ping.html) and reach out to %<engineers_group>s group for a review.
Please check the ~"product intelligence" [guide](https://docs.gitlab.com/ee/development/usage_ping.html) and reach out to %<engineers_group>s group for a review.
%<changed_files>s

View File

@ -8,7 +8,7 @@
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/operations/alert_management.html
url: https://docs.gitlab.com/ee/operations/incident_management/index.html
image_url: https://about.gitlab.com/images/13_1/alert_management.png
published_at: 2020-06-22
release: 13.1

View File

@ -655,15 +655,41 @@ Non-determinism is the breeding ground for flaky and brittle specs. Such specs e
### Faking `Date` for determinism
Consider using `useFakeDate` to ensure a consistent value is returned with every `new Date()` or `Date.now()`.
`Date` is faked by default in our Jest environment. This means every call to `Date()` or `Date.now()` returns a fixed deterministic value.
If you really need to change the default fake date, you can call `useFakeDate` within any `describe` block, and
the date will be replaced for that specs within that `describe` context only:
```javascript
import { useFakeDate } from 'helpers/fake_date';
describe('cool/component', () => {
useFakeDate();
// Default fake `Date`
const TODAY = new Date();
// ...
// NOTE: `useFakeDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.).
describe("on Ada Lovelace's Birthday", () => {
useFakeDate(1815, 11, 10)
it('Date is no longer default', () => {
expect(new Date()).not.toEqual(TODAY);
});
});
it('Date is still default in this scope', () => {
expect(new Date()).toEqual(TODAY)
});
})
```
Similarly, if you really need to use the real `Date` class, then you can import and call `useRealDate` within any `describe` block:
```javascript
import { useRealDate } from 'helpers/fake_date';
// NOTE: `useRealDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.).
describe('with real date', () => {
useRealDate();
});
```

View File

@ -110,6 +110,14 @@ values extracted from the [`alerts` field in webhook payload](https://prometheus
- `full_query`: Alert query extracted from the payload's `generatorURL` field
- Optional list of attached annotations extracted from `annotations/*`
- Alert [GFM](../../user/markdown.md): GitLab Flavored Markdown from the payload's `annotations/gitlab_incident_markdown` field.
- Alert Severity (introduced in GitLab version [13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50871):
Extracted from the alert payload field `labels/severity`. Maps case-insensitive
value to [Alert's severity](../incident_management/alerts.md#alert-severity):
- **Critical**: `critical`, `s1`, `p1`, `emergency`, `fatal`, or any value not in this list
- **High**: `high`, `s2`, `p2`, `major`, `page`
- **Medium**: `medium`, `s3`, `p3`, `error`, `alert`
- **Low**: `low`, `s4`, `p4`, `warn`, `warning`
- **Info**: `info`, `s5`, `p5`, `debug`, `information`, `notice`
When GitLab receives a **Recovery Alert**, it closes the associated issue.
This action is recorded as a system message on the issue indicating that it

View File

@ -1285,6 +1285,7 @@ You may need to reconfigure or restart GitLab for the changes to take effect.
UPDATE namespaces SET runners_token = null, runners_token_encrypted = null;
-- Clear instance tokens
UPDATE application_settings SET runners_registration_token_encrypted = null;
UPDATE application_settings SET encrypted_ci_jwt_signing_key = null;
-- Clear runner tokens
UPDATE ci_runners SET token = null, token_encrypted = null;
```

View File

@ -269,6 +269,7 @@ module API
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Services
mount ::API::Settings

View File

@ -18,7 +18,7 @@ module API
default_format :json
authenticate_with do |accept|
accept.token_types(:personal_access_token, :deploy_token, :job_token)
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
.sent_through(:http_basic_auth)
end

View File

@ -20,7 +20,7 @@ module API
default_format :json
authenticate_with do |accept|
accept.token_types(:personal_access_token, :deploy_token, :job_token)
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
.sent_through(:http_basic_auth)
end

102
lib/api/rubygem_packages.rb Normal file
View File

@ -0,0 +1,102 @@
# frozen_string_literal: true
###
# API endpoints for the RubyGem package registry
module API
class RubygemPackages < ::API::Base
include ::API::Helpers::Authentication
helpers ::API::Helpers::PackagesHelpers
feature_category :package_registry
# The Marshal version can be found by "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
# Updating the version should require a GitLab API version change.
MARSHAL_VERSION = '4.8'
FILE_NAME_REQUIREMENTS = {
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
content_type :binary, 'application/octet-stream'
authenticate_with do |accept|
accept.token_types(:personal_access_token, :deploy_token, :job_token)
.sent_through(:http_token)
end
before do
require_packages_enabled!
authenticate!
not_found! unless Feature.enabled?(:rubygem_packages, user_project)
end
params do
requires :id, type: String, desc: 'The ID or full path of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/rubygems' do
desc 'Download the spec index file' do
detail 'This feature was introduced in GitLab 13.9'
end
params do
requires :file_name, type: String, desc: 'Spec file name'
end
get ":file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299267
not_found!
end
desc 'Download the gemspec file' do
detail 'This feature was introduced in GitLab 13.9'
end
params do
requires :file_name, type: String, desc: 'Gemspec file name'
end
get "quick/Marshal.#{MARSHAL_VERSION}/:file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299284
not_found!
end
desc 'Download the .gem package' do
detail 'This feature was introduced in GitLab 13.9'
end
params do
requires :file_name, type: String, desc: 'Package file name'
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299283
not_found!
end
namespace 'api/v1' do
desc 'Authorize a gem upload from workhorse' do
detail 'This feature was introduced in GitLab 13.9'
end
post 'gems/authorize' do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299263
not_found!
end
desc 'Upload a gem' do
detail 'This feature was introduced in GitLab 13.9'
end
post 'gems' do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299263
not_found!
end
desc 'Fetch a list of dependencies' do
detail 'This feature was introduced in GitLab 13.9'
end
params do
optional :gems, type: String, desc: 'Comma delimited gem names'
end
get 'dependencies' do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299282
not_found!
end
end
end
end
end
end

View File

@ -133,7 +133,7 @@ class Feature
# This method is called from config/initializers/flipper.rb and can be used
# to register Flipper groups.
# See https://docs.gitlab.com/ee/development/feature_flags.html#feature-groups
# See https://docs.gitlab.com/ee/development/feature_flags/index.html
def register_feature_groups
end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
# Attribute mapping for alerts via prometheus alerting integration.
module Gitlab
module AlertManagement
module Payload
# Attribute mapping for alerts via prometheus alerting integration.
class Prometheus < Base
attribute :alert_markdown, paths: %w(annotations gitlab_incident_markdown)
attribute :annotations, paths: 'annotations'
@ -26,13 +26,49 @@ module Gitlab
paths: [%w(annotations title),
%w(annotations summary),
%w(labels alertname)]
attribute :starts_at_raw,
paths: [%w(startsAt)]
private :starts_at_raw
attribute :severity_raw, paths: %w(labels severity)
private :severity_raw
METRIC_TIME_WINDOW = 30.minutes
SEVERITY_MAP = {
'critical' => :critical,
'high' => :high,
'medium' => :medium,
'low' => :low,
'info' => :info,
's1' => :critical,
's2' => :high,
's3' => :medium,
's4' => :low,
's5' => :info,
'p1' => :critical,
'p2' => :high,
'p3' => :medium,
'p4' => :low,
'p5' => :info,
'debug' => :info,
'information' => :info,
'notice' => :info,
'warn' => :low,
'warning' => :low,
'minor' => :low,
'error' => :medium,
'major' => :high,
'emergency' => :critical,
'fatal' => :critical,
'alert' => :medium,
'page' => :high
}.freeze
# Handle an unmapped severity value the same way we treat missing values
# so we can fallback to alert's default severity `critical`.
UNMAPPED_SEVERITY = nil
def monitoring_tool
Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
end
@ -65,6 +101,12 @@ module Gitlab
project && title && starts_at_raw
end
def severity
return unless severity_raw
SEVERITY_MAP.fetch(severity_raw.to_s.downcase, UNMAPPED_SEVERITY)
end
private
def plain_gitlab_fingerprint

View File

@ -10,7 +10,7 @@ module Gitlab
attr_reader :location
validates :location, inclusion: { in: %i[http_basic_auth] }
validates :location, inclusion: { in: %i[http_basic_auth http_token] }
def initialize(location)
@location = location
@ -21,6 +21,8 @@ module Gitlab
case @location
when :http_basic_auth
extract_from_http_basic_auth request
when :http_token
extract_from_http_token request
end
end
@ -32,6 +34,13 @@ module Gitlab
UsernameAndPassword.new(username, password)
end
def extract_from_http_token(request)
password = request.headers['Authorization']
return unless password.present?
UsernameAndPassword.new(nil, password)
end
end
end
end

View File

@ -7,7 +7,16 @@ module Gitlab
attr_reader :token_type
validates :token_type, inclusion: { in: %i[personal_access_token job_token deploy_token] }
validates :token_type, inclusion: {
in: %i[
personal_access_token_with_username
job_token_with_username
deploy_token_with_username
personal_access_token
job_token
deploy_token
]
}
def initialize(token_type)
@token_type = token_type
@ -38,49 +47,94 @@ module Gitlab
when :deploy_token
resolve_deploy_token raw
when :personal_access_token_with_username
resolve_personal_access_token_with_username raw
when :job_token_with_username
resolve_job_token_with_username raw
when :deploy_token_with_username
resolve_deploy_token_with_username raw
end
end
private
def resolve_personal_access_token(raw)
# Check if the password is a personal access token
pat = ::PersonalAccessToken.find_by_token(raw.password)
return unless pat
def resolve_personal_access_token_with_username(raw)
raise ::Gitlab::Auth::UnauthorizedError unless raw.username
# Ensure that the username matches the token. This check is a subtle
# departure from the existing behavior of #find_personal_access_token_from_http_basic_auth.
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38627#note_435907856
raise ::Gitlab::Auth::UnauthorizedError unless pat.user.username == raw.username
with_personal_access_token(raw) do |pat|
break unless pat
pat
# Ensure that the username matches the token. This check is a subtle
# departure from the existing behavior of #find_personal_access_token_from_http_basic_auth.
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38627#note_435907856
raise ::Gitlab::Auth::UnauthorizedError unless pat.user.username == raw.username
pat
end
end
def resolve_job_token(raw)
def resolve_job_token_with_username(raw)
# Only look for a job if the username is correct
return if ::Gitlab::Auth::CI_JOB_USER != raw.username
job = ::Ci::AuthJobFinder.new(token: raw.password).execute
with_job_token(raw) do |job|
job
end
end
# Actively reject credentials with the username `gitlab-ci-token` if
# the password is not a valid job token. This replicates existing
# behavior of #find_user_from_job_token.
raise ::Gitlab::Auth::UnauthorizedError unless job
def resolve_deploy_token_with_username(raw)
with_deploy_token(raw) do |token|
break unless token
job
# Ensure that the username matches the token. This check is a subtle
# departure from the existing behavior of #deploy_token_from_request.
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38627#note_474826205
raise ::Gitlab::Auth::UnauthorizedError unless token.username == raw.username
token
end
end
def resolve_personal_access_token(raw)
with_personal_access_token(raw) do |pat|
pat
end
end
def resolve_job_token(raw)
with_job_token(raw) do |job|
job
end
end
def resolve_deploy_token(raw)
# Check if the password is a deploy token
with_deploy_token(raw) do |token|
token
end
end
def with_personal_access_token(raw, &block)
pat = ::PersonalAccessToken.find_by_token(raw.password)
return unless pat
yield(pat)
end
def with_deploy_token(raw, &block)
token = ::DeployToken.active.find_by_token(raw.password)
return unless token
# Ensure that the username matches the token. This check is a subtle
# departure from the existing behavior of #deploy_token_from_request.
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38627#note_474826205
raise ::Gitlab::Auth::UnauthorizedError unless token.username == raw.username
yield(token)
end
token
def with_job_token(raw, &block)
job = ::Ci::AuthJobFinder.new(token: raw.password).execute
raise ::Gitlab::Auth::UnauthorizedError unless job
yield(job)
end
end
end

View File

@ -40,14 +40,14 @@ verify:jdk8:
<<: *verify
# To deploy packages from CI, create a ci_settings.xml file
# For deploying packages to GitLab's Maven Repository: See https://docs.gitlab.com/ee/user/project/packages/maven_repository.html#creating-maven-packages-with-gitlab-cicd for more details.
# For deploying packages to GitLab's Maven Repository: See https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#create-maven-packages-with-gitlab-cicd for more details.
# Please note: The GitLab Maven Repository is currently only available in GitLab Premium / Ultimate.
# For `master` branch run `mvn deploy` automatically.
deploy:jdk8:
stage: deploy
script:
- if [ ! -f ci_settings.xml ];
then echo "CI settings missing\! If deploying to GitLab Maven Repository, please see https://docs.gitlab.com/ee/user/project/packages/maven_repository.html#creating-maven-packages-with-gitlab-cicd for instructions.";
then echo "CI settings missing\! If deploying to GitLab Maven Repository, please see https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#create-maven-packages-with-gitlab-cicd for instructions.";
fi
- 'mvn $MAVEN_CLI_OPTS deploy -s ci_settings.xml'
only:

View File

@ -1,4 +1,4 @@
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/license_compliance/
# Read more about this feature here: https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html
#
# Configure the scanning tool through the environment variables.
# List of the variables: https://gitlab.com/gitlab-org/security-products/analyzers/license-finder#settings

View File

@ -8,8 +8,9 @@ module Gitlab
include TSort
include Gitlab::Utils::StrongMemoize
def initialize(variables)
def initialize(variables, project)
@variables = variables
@project = project
end
def valid?
@ -19,7 +20,7 @@ module Gitlab
# errors sorts an array of variables, ignoring unknown variable references,
# and returning an error string if a circular variable reference is found
def errors
return if Feature.disabled?(:variable_inside_variable)
return if Feature.disabled?(:variable_inside_variable, @project)
strong_memoize(:errors) do
# Check for cyclic dependencies and build error message in that case
@ -34,7 +35,7 @@ module Gitlab
# sort sorts an array of variables, ignoring unknown variable references.
# If a circular variable reference is found, the original array is returned
def sort
return @variables if Feature.disabled?(:variable_inside_variable)
return @variables if Feature.disabled?(:variable_inside_variable, @project)
return @variables if errors
tsort

View File

@ -33,6 +33,7 @@ module Gitlab
gon.suggested_label_colors = LabelsHelper.suggested_colors
gon.first_day_of_week = current_user&.first_day_of_week || Gitlab::CurrentSettings.first_day_of_week
gon.ee = Gitlab.ee?
gon.dot_com = Gitlab.com?
if current_user
gon.current_user_id = current_user.id

View File

@ -48,3 +48,13 @@
'incident_management_incident_unrelate',
'incident_management_incident_change_confidential'
]
- name: i_testing_paid_monthly_active_user_total
operator: OR
source: redis
events: [
'i_testing_web_performance_widget_total',
'i_testing_full_code_quality_report_total',
'i_testing_group_code_coverage_visit_total',
'i_testing_load_performance_widget_total',
'i_testing_metrics_report_widget_total'
]

View File

@ -7567,9 +7567,6 @@ msgstr ""
msgid "ConfluenceService|Your GitLab Wiki can be accessed here: %{wiki_link}. To re-enable your GitLab Wiki, disable this integration"
msgstr ""
msgid "Congratulations! You have enabled Two-factor Authentication!"
msgstr ""
msgid "Congratulations, your free trial is activated."
msgstr ""
@ -15747,9 +15744,6 @@ msgstr ""
msgid "Install GitLab Runner on Kubernetes"
msgstr ""
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
msgid "Install on clusters"
msgstr ""
@ -32543,6 +32537,9 @@ msgstr ""
msgid "We heard back from your device. You have been authenticated."
msgstr ""
msgid "We recommend cloud-based mobile authenticator apps such as Authy, Duo Mobile, and LastPass. They can restore access if you lose your hardware device."
msgstr ""
msgid "We recommend that you buy more Pipeline minutes to avoid any interruption of service."
msgstr ""
@ -33457,6 +33454,9 @@ msgstr ""
msgid "You have reached your project limit"
msgstr ""
msgid "You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can %{anchorOpen}use that key to generate additional recovery codes%{anchorClose}."
msgstr ""
msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. Youll receive a receipt via email."
msgstr ""

View File

@ -216,7 +216,7 @@ run all the tests in the `Test::Instance::All` scenario, and then enable the
feature flag again if it was enabled earlier.
Note: the QA framework doesn't currently allow you to easily toggle a feature
flag during a single test, [as you can in unit tests](https://docs.gitlab.com/ee/development/feature_flags.html#specs),
flag during a single test, [as you can in unit tests](https://docs.gitlab.com/ee/development/feature_flags/index.html),
but [that capability is planned](https://gitlab.com/gitlab-org/quality/team-tasks/issues/77).
Note also that the `--` separator isn't used because `--enable-feature` and `--disable-feature`

View File

@ -765,7 +765,7 @@ RSpec.describe 'Login' do
click_link 'Proceed'
expect(current_path).to eq(profile_account_path)
expect(page).to have_content('Congratulations! You have enabled Two-factor Authentication!')
expect(page).to have_content('You have set up 2FA for your account! If you lose access to your 2FA device, you can use your recovery codes to access your account. Alternatively, if you upload an SSH key, you can use that key to generate additional recovery codes.')
end
end

View File

@ -1,11 +1,13 @@
// Frida Kahlo's birthday (6 = July)
export const DEFAULT_ARGS = [2020, 6, 6];
const DEFAULT_ARGS = [2020, 6, 6];
const RealDate = Date;
const isMocked = (val) => Boolean(val.mock);
export const createFakeDateClass = (ctorDefault) => {
const createFakeDateClass = (ctorDefaultParam = []) => {
const ctorDefault = ctorDefaultParam.length ? ctorDefaultParam : DEFAULT_ARGS;
const FakeDate = new Proxy(RealDate, {
construct: (target, argArray) => {
const ctorArgs = argArray.length ? argArray : ctorDefault;
@ -39,11 +41,20 @@ export const createFakeDateClass = (ctorDefault) => {
return FakeDate;
};
export const useFakeDate = (...args) => {
const FakeDate = createFakeDateClass(args.length ? args : DEFAULT_ARGS);
const setGlobalDateToFakeDate = (...args) => {
const FakeDate = createFakeDateClass(args);
global.Date = FakeDate;
};
export const useRealDate = () => {
const setGlobalDateToRealDate = () => {
global.Date = RealDate;
};
// We use commonjs so that the test environment module can pick this up
// eslint-disable-next-line import/no-commonjs
module.exports = {
setGlobalDateToFakeDate,
setGlobalDateToRealDate,
createFakeDateClass,
RealDate,
};

View File

@ -1,15 +1,11 @@
import { createFakeDateClass, DEFAULT_ARGS, useRealDate } from './fake_date';
import { createFakeDateClass } from './fake_date';
describe('spec/helpers/fake_date', () => {
describe('createFakeDateClass', () => {
let FakeDate;
beforeAll(() => {
useRealDate();
});
beforeEach(() => {
FakeDate = createFakeDateClass(DEFAULT_ARGS);
FakeDate = createFakeDateClass();
});
it('should use default args', () => {

View File

@ -0,0 +1,2 @@
export * from './fake_date';
export * from './jest';

View File

@ -0,0 +1,41 @@
import { createJestExecutionWatcher } from '../jest_execution_watcher';
import { RealDate, createFakeDateClass } from './fake_date';
const throwInsideExecutionError = (fnName) => {
throw new Error(`Cannot call "${fnName}" during test execution (i.e. within "it", "beforeEach", "beforeAll", etc.).
Instead, please move the call to "${fnName}" inside the "describe" block itself.
describe('', () => {
+ ${fnName}();
it('', () => {
- ${fnName}();
})
})
`);
};
const isExecutingTest = createJestExecutionWatcher();
export const useDateInScope = (fnName, factory) => {
if (isExecutingTest()) {
throwInsideExecutionError(fnName);
}
let origDate;
beforeAll(() => {
origDate = global.Date;
global.Date = factory();
});
afterAll(() => {
global.Date = origDate;
});
};
export const useFakeDate = (...args) =>
useDateInScope('useFakeDate', () => createFakeDateClass(args));
export const useRealDate = () => useDateInScope('useRealDate', () => RealDate);

View File

@ -0,0 +1,12 @@
export const createJestExecutionWatcher = () => {
let isExecuting = false;
beforeAll(() => {
isExecuting = true;
});
afterAll(() => {
isExecuting = false;
});
return () => isExecuting;
};

View File

@ -1,10 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
import ExpiresAtField from '~/access_tokens/components/expires_at_field.vue';
describe('~/access_tokens/components/expires_at_field', () => {
useFakeDate();
let wrapper;
const createComponent = () => {

View File

@ -3,7 +3,6 @@ import { GlLineChart } from '@gitlab/ui/dist/charts';
import { GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useFakeDate } from 'helpers/fake_date';
import ProjectsAndGroupChart from '~/analytics/instance_statistics/components/projects_and_groups_chart.vue';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
import projectsQuery from '~/analytics/instance_statistics/graphql/queries/projects.query.graphql';
@ -45,8 +44,8 @@ describe('ProjectsAndGroupChart', () => {
return shallowMount(ProjectsAndGroupChart, {
props: {
startDate: useFakeDate(2020, 9, 26),
endDate: useFakeDate(2020, 10, 1),
startDate: new Date(2020, 9, 26),
endDate: new Date(2020, 10, 1),
totalDataPoints: mockCountsData2.length,
},
localVue,

View File

@ -3,7 +3,6 @@ import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useFakeDate } from 'helpers/fake_date';
import UsersChart from '~/analytics/instance_statistics/components/users_chart.vue';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
import usersQuery from '~/analytics/instance_statistics/graphql/queries/users.query.graphql';
@ -31,8 +30,8 @@ describe('UsersChart', () => {
return shallowMount(UsersChart, {
props: {
startDate: useFakeDate(2020, 9, 26),
endDate: useFakeDate(2020, 10, 1),
startDate: new Date(2020, 9, 26),
endDate: new Date(2020, 10, 1),
totalDataPoints: mockCountsData2.length,
},
localVue,

View File

@ -69,7 +69,7 @@ describe('CompareDropdownLayout', () => {
expect(findListItemsData()).toEqual([
{
href: 'version/1',
text: 'version 1 (base) abcdef1 1 commit 2 years ago',
text: 'version 1 (base) abcdef1 1 commit 1 year ago',
createdAt: TEST_CREATED_AT,
isActive: true,
},

View File

@ -4,6 +4,7 @@ const path = require('path');
const { ErrorWithStack } = require('jest-util');
const JSDOMEnvironment = require('jest-environment-jsdom');
const { TEST_HOST } = require('./__helpers__/test_constants');
const { setGlobalDateToFakeDate } = require('./__helpers__/fake_date/fake_date');
const ROOT_PATH = path.resolve(__dirname, '../..');
@ -12,6 +13,10 @@ class CustomEnvironment extends JSDOMEnvironment {
// Setup testURL so that window.location is setup properly
super({ ...config, testURL: TEST_HOST }, context);
// Fake the `Date` for `jsdom` which fixes things like document.cookie
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39496#note_503084332
setGlobalDateToFakeDate();
Object.assign(context.console, {
error(...args) {
throw new ErrorWithStack(

View File

@ -298,9 +298,7 @@ describe('ErrorTrackingList', () => {
});
it('shows empty state', () => {
expect(wrapper.find('a').attributes('href')).toBe(
'/help/user/project/operations/error_tracking.html',
);
expect(wrapper.find(GlEmptyState).isVisible()).toBe(true);
});
});

View File

@ -3,6 +3,7 @@ import Vue from 'vue';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { useRealDate } from 'helpers/fake_date';
import axios from '~/lib/utils/axios_utils';
import appComponent from '~/frequent_items/components/app.vue';
import eventHub from '~/frequent_items/event_hub';
@ -93,23 +94,27 @@ describe('Frequent Items App Component', () => {
expect(projects.length).toBe(1);
});
it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
let projects;
const newTimestamp = Date.now() + HOUR_IN_MS + 1;
describe('with real date', () => {
useRealDate();
vm.logItemAccess(session.storageKey, session.project);
projects = JSON.parse(storage[session.storageKey]);
it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
let projects;
const newTimestamp = Date.now() + HOUR_IN_MS + 1;
expect(projects[0].frequency).toBe(1);
vm.logItemAccess(session.storageKey, session.project);
projects = JSON.parse(storage[session.storageKey]);
vm.logItemAccess(session.storageKey, {
...session.project,
lastAccessedOn: newTimestamp,
expect(projects[0].frequency).toBe(1);
vm.logItemAccess(session.storageKey, {
...session.project,
lastAccessedOn: newTimestamp,
});
projects = JSON.parse(storage[session.storageKey]);
expect(projects[0].frequency).toBe(2);
expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
});
projects = JSON.parse(storage[session.storageKey]);
expect(projects[0].frequency).toBe(2);
expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
});
it('should always update project metadata', () => {

View File

@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import { GlLink, GlLabel, GlIcon, GlFormCheckbox } from '@gitlab/ui';
import { useFakeDate } from 'helpers/fake_date';
import IssuableItem from '~/issuable_list/components/issuable_item.vue';
import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
@ -19,6 +19,9 @@ const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots
});
describe('IssuableItem', () => {
// The mock data is dependent that this is after our default date
useFakeDate(2020, 11, 11);
const mockLabels = mockIssuable.labels.nodes;
const mockAuthor = mockIssuable.author;
const originalUrl = gon.gitlab_url;

View File

@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
import IssuableBody from '~/issuable_show/components/issuable_body.vue';
@ -35,6 +36,9 @@ const createComponent = (propsData = issuableBodyProps) =>
});
describe('IssuableBody', () => {
// Some assertions expect a date later than our default
useFakeDate(2020, 11, 11);
let wrapper;
beforeEach(() => {
@ -98,11 +102,8 @@ describe('IssuableBody', () => {
it('renders issuable edit info', () => {
const editedEl = wrapper.find('small');
const sanitizedText = editedEl.text().replace(/\n/g, ' ').replace(/\s+/g, ' ');
expect(sanitizedText).toContain('Edited');
expect(sanitizedText).toContain('ago');
expect(sanitizedText).toContain(`by ${mockIssuable.updatedBy.name}`);
expect(editedEl.text()).toMatchInterpolatedText('Edited 3 months ago by Administrator');
});
it('renders issuable-edit-form when `editFormVisible` prop is true', async () => {

View File

@ -12,9 +12,15 @@ import { simpleIssue, testAssignees, testLabels } from '../issuable_list_test_da
jest.mock('~/user_popovers');
const TEST_NOW = '2019-08-28T20:03:04.713Z';
const TEST_MONTH_AGO = '2019-07-28';
const TEST_MONTH_LATER = '2019-09-30';
const TODAY = new Date();
const createTestDateFromDelta = (timeDelta) =>
formatDate(new Date(TODAY.getTime() + timeDelta), 'yyyy-mm-dd');
// TODO: Encapsulate date helpers https://gitlab.com/gitlab-org/gitlab/-/issues/320883
const MONTHS_IN_MS = 1000 * 60 * 60 * 24 * 31;
const TEST_MONTH_AGO = createTestDateFromDelta(-MONTHS_IN_MS);
const TEST_MONTH_LATER = createTestDateFromDelta(MONTHS_IN_MS);
const DATE_FORMAT = 'mmm d, yyyy';
const TEST_USER_NAME = 'Tyler Durden';
const TEST_BASE_URL = `${TEST_HOST}/issues`;
@ -26,16 +32,8 @@ const TEST_MILESTONE = {
const TEXT_CLOSED = 'CLOSED';
const TEST_META_COUNT = 100;
// Use FixedDate so that time sensitive info in snapshots don't fail
class FixedDate extends Date {
constructor(date = TEST_NOW) {
super(date);
}
}
describe('Issuable component', () => {
let issuable;
let DateOrig;
let wrapper;
const factory = (props = {}, scopedLabelsAvailable = false) => {
@ -63,15 +61,6 @@ describe('Issuable component', () => {
wrapper = null;
});
beforeAll(() => {
DateOrig = window.Date;
window.Date = FixedDate;
});
afterAll(() => {
window.Date = DateOrig;
});
const checkExists = (findFn) => () => findFn().exists();
const hasIcon = (iconName, iconWrapper = wrapper) =>
iconWrapper.findAll(GlIcon).wrappers.some((icon) => icon.props('name') === iconName);

View File

@ -8,19 +8,9 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const originalRelease = getJSONFixture('api/releases/release.json');
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
jest.mock('~/vue_shared/mixins/timeago', () => ({
methods: {
timeFormatted() {
return mockIsFutureRelease ? 'in 1 month' : '7 fortnights ago';
},
tooltipTitle() {
return 'February 30, 2401';
},
},
}));
// TODO: Encapsulate date helpers https://gitlab.com/gitlab-org/gitlab/-/issues/320883
const MONTHS_IN_MS = 1000 * 60 * 60 * 24 * 31;
const mockFutureDate = new Date(new Date().getTime() + MONTHS_IN_MS).toISOString();
describe('Release block footer', () => {
let wrapper;
@ -44,7 +34,6 @@ describe('Release block footer', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockIsFutureRelease = false;
});
const commitInfoSection = () => wrapper.find('.js-commit-info');
@ -88,7 +77,7 @@ describe('Release block footer', () => {
it('renders the author and creation time info', () => {
expect(trimText(authorDateInfoSection().text())).toBe(
`Created 7 fortnights ago by ${release.author.username}`,
`Created 1 year ago by ${release.author.username}`,
);
});
@ -100,7 +89,6 @@ describe('Release block footer', () => {
describe('renders the author and creation time info with future release date', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ releasedAt: mockFutureDate });
});
@ -113,7 +101,6 @@ describe('Release block footer', () => {
describe('when the release date is in the future', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ releasedAt: mockFutureDate });
});
@ -177,13 +164,12 @@ describe('Release block footer', () => {
beforeEach(() => factory({ author: undefined }));
it('renders the release date without the author name', () => {
expect(trimText(authorDateInfoSection().text())).toBe(`Created 7 fortnights ago`);
expect(trimText(authorDateInfoSection().text())).toBe(`Created 1 year ago`);
});
});
describe('future release without any author info', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ author: undefined, releasedAt: mockFutureDate });
});

View File

@ -3,6 +3,7 @@ import 'jquery';
import * as jqueryMatchers from 'custom-jquery-matchers';
import { config as testUtilsConfig } from '@vue/test-utils';
import { setGlobalDateToFakeDate } from 'helpers/fake_date';
import Translate from '~/vue_shared/translate';
import { initializeTestTimeout } from './__helpers__/timeout';
import { getJSONFixture, loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures';
@ -20,6 +21,10 @@ process.on('unhandledRejection', global.promiseRejectionHandler);
setupManualMocks();
// Fake the `Date` for the rest of the jest spec runtime environment.
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39496#note_503084332
setGlobalDateToFakeDate();
afterEach(() =>
// give Promises a bit more time so they fail the right test
new Promise(setImmediate).then(() => {

View File

@ -156,8 +156,6 @@ RSpec.describe Gitlab::AlertManagement::Payload::Prometheus do
end
describe '#gitlab_fingerprint' do
subject { parsed_payload.gitlab_fingerprint }
let(:raw_payload) do
{
'startsAt' => Time.current.to_s,
@ -166,6 +164,8 @@ RSpec.describe Gitlab::AlertManagement::Payload::Prometheus do
}
end
subject { parsed_payload.gitlab_fingerprint }
it 'returns a fingerprint' do
plain_fingerprint = [
parsed_payload.send(:starts_at_raw),
@ -237,4 +237,65 @@ RSpec.describe Gitlab::AlertManagement::Payload::Prometheus do
it { is_expected.to be_falsey }
end
end
describe '#severity' do
using RSpec::Parameterized::TableSyntax
let(:raw_payload) { { 'labels' => { 'severity' => payload_severity } } }
subject { parsed_payload.severity }
context 'when set' do
where(:payload_severity, :expected_severity) do
'critical' | :critical
'high' | :high
'medium' | :medium
'low' | :low
'info' | :info
's1' | :critical
's2' | :high
's3' | :medium
's4' | :low
's5' | :info
'p1' | :critical
'p2' | :high
'p3' | :medium
'p4' | :low
'p5' | :info
'CRITICAL' | :critical
'cRiTiCaL' | :critical
'S1' | :critical
'unmapped' | nil
1 | nil
nil | nil
'debug' | :info
'information' | :info
'notice' | :info
'warn' | :low
'warning' | :low
'minor' | :low
'error' | :medium
'major' | :high
'emergency' | :critical
'fatal' | :critical
'alert' | :medium
'page' | :high
end
with_them do
it { is_expected.to eq(expected_severity) }
end
end
context 'without key' do
let(:raw_payload) { {} }
it { is_expected.to be_nil }
end
end
end

View File

@ -51,5 +51,26 @@ RSpec.describe Gitlab::APIAuthentication::TokenLocator do
end
end
end
context 'with :http_token' do
let(:type) { :http_token }
context 'without credentials' do
let(:request) { double(headers: {}) }
it 'returns nil' do
expect(subject).to be(nil)
end
end
context 'with credentials' do
let(:password) { 'bar' }
let(:request) { double(headers: { "Authorization" => password }) }
it 'returns the credentials' do
expect(subject.password).to eq(password)
end
end
end
end
end

View File

@ -47,8 +47,8 @@ RSpec.describe Gitlab::APIAuthentication::TokenResolver do
subject { resolver.resolve(raw) }
context 'with :personal_access_token' do
let(:type) { :personal_access_token }
context 'with :personal_access_token_with_username' do
let(:type) { :personal_access_token_with_username }
let(:token) { personal_access_token }
context 'with valid credentials' do
@ -62,10 +62,16 @@ RSpec.describe Gitlab::APIAuthentication::TokenResolver do
it_behaves_like 'an unauthorized request'
end
context 'with no username' do
let(:raw) { username_and_password(nil, token.token) }
it_behaves_like 'an unauthorized request'
end
end
context 'with :job_token' do
let(:type) { :job_token }
context 'with :job_token_with_username' do
let(:type) { :job_token_with_username }
let(:token) { ci_job }
context 'with valid credentials' do
@ -93,8 +99,8 @@ RSpec.describe Gitlab::APIAuthentication::TokenResolver do
end
end
context 'with :deploy_token' do
let(:type) { :deploy_token }
context 'with :deploy_token_with_username' do
let(:type) { :deploy_token_with_username }
let(:token) { deploy_token }
context 'with a valid deploy token' do
@ -109,6 +115,51 @@ RSpec.describe Gitlab::APIAuthentication::TokenResolver do
it_behaves_like 'an unauthorized request'
end
end
context 'with :personal_access_token' do
let(:type) { :personal_access_token }
let(:token) { personal_access_token }
context 'with valid credentials' do
let(:raw) { username_and_password(nil, token.token) }
it_behaves_like 'an authorized request'
end
end
context 'with :job_token' do
let(:type) { :job_token }
let(:token) { ci_job }
context 'with valid credentials' do
let(:raw) { username_and_password(nil, token.token) }
it_behaves_like 'an authorized request'
end
context 'when the job is not running' do
let(:raw) { username_and_password(nil, ci_job_done.token) }
it_behaves_like 'an unauthorized request'
end
context 'with an invalid job token' do
let(:raw) { username_and_password(nil, "not a valid CI job token") }
it_behaves_like 'an unauthorized request'
end
end
context 'with :deploy_token' do
let(:type) { :deploy_token }
let(:token) { deploy_token }
context 'with a valid deploy token' do
let(:raw) { username_and_password(nil, token.token) }
it_behaves_like 'an authorized request'
end
end
end
def username_and_password(username, password)

View File

@ -5,8 +5,11 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
describe '#errors' do
context 'when FF :variable_inside_variable is disabled' do
let_it_be(:project_with_flag_disabled) { create(:project) }
let_it_be(:project_with_flag_enabled) { create(:project) }
before do
stub_feature_flags(variable_inside_variable: false)
stub_feature_flags(variable_inside_variable: [project_with_flag_enabled])
end
context 'table tests' do
@ -53,7 +56,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project_with_flag_disabled) }
it 'does not report error' do
expect(subject.errors).to eq(nil)
@ -67,8 +70,11 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
end
context 'when FF :variable_inside_variable is enabled' do
let_it_be(:project_with_flag_disabled) { create(:project) }
let_it_be(:project_with_flag_enabled) { create(:project) }
before do
stub_feature_flags(variable_inside_variable: true)
stub_feature_flags(variable_inside_variable: [project_with_flag_enabled])
end
context 'table tests' do
@ -100,7 +106,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project_with_flag_enabled) }
it 'errors matches expected validation result' do
expect(subject.errors).to eq(validation_result)
@ -164,7 +170,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
let_it_be(:project) { create(:project) }
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project) }
it 'does not expand variables' do
expect(subject.sort).to eq(variables)
@ -239,7 +246,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
let_it_be(:project) { create(:project) }
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables, project) }
it 'sort returns correctly sorted variables' do
expect(subject.sort.map { |var| var[:key] }).to eq(result)

View File

@ -463,7 +463,7 @@ RSpec.describe JiraService do
let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields" }
it 'calls the Jira API with the options to get the issue' do
jira_service.find_issue(issue_key, { expand: 'renderedFields' })
jira_service.find_issue(issue_key, rendered_fields: true)
expect(WebMock).to have_requested(:get, issue_url)
end

View File

@ -17,7 +17,7 @@ RSpec.describe Ci::BuildRunnerPresenter do
describe '#artifacts' do
context "when option contains archive-type artifacts" do
let(:build) { create(:ci_build, options: { artifacts: archive } ) }
let(:build) { create(:ci_build, options: { artifacts: archive }) }
it 'presents correct hash' do
expect(presenter.artifacts.first).to include(archive_expectation)
@ -249,7 +249,7 @@ RSpec.describe Ci::BuildRunnerPresenter do
it 'returns the correct refspecs' do
is_expected.to contain_exactly("+#{pipeline.sha}:refs/pipelines/#{pipeline.id}",
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
end
end
end

View File

@ -0,0 +1,139 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::RubygemPackages do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project) }
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:user) { personal_access_token.user }
let_it_be(:job) { create(:ci_build, :running, user: user) }
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:headers) { {} }
shared_examples 'when feature flag is disabled' do
let(:headers) do
{ 'HTTP_AUTHORIZATION' => personal_access_token.token }
end
before do
stub_feature_flags(rubygem_packages: false)
end
it_behaves_like 'returning response status', :not_found
end
shared_examples 'when package feature is disabled' do
before do
stub_config(packages: { enabled: false })
end
it_behaves_like 'returning response status', :not_found
end
shared_examples 'without authentication' do
it_behaves_like 'returning response status', :unauthorized
end
shared_examples 'with authentication' do
let(:headers) do
{ 'HTTP_AUTHORIZATION' => token }
end
let(:tokens) do
{
personal_access_token: personal_access_token.token,
deploy_token: deploy_token.token,
job_token: job.token
}
end
where(:user_role, :token_type, :valid_token, :status) do
:guest | :personal_access_token | true | :not_found
:guest | :personal_access_token | false | :unauthorized
:guest | :deploy_token | true | :not_found
:guest | :deploy_token | false | :unauthorized
:guest | :job_token | true | :not_found
:guest | :job_token | false | :unauthorized
:reporter | :personal_access_token | true | :not_found
:reporter | :personal_access_token | false | :unauthorized
:reporter | :deploy_token | true | :not_found
:reporter | :deploy_token | false | :unauthorized
:reporter | :job_token | true | :not_found
:reporter | :job_token | false | :unauthorized
:developer | :personal_access_token | true | :not_found
:developer | :personal_access_token | false | :unauthorized
:developer | :deploy_token | true | :not_found
:developer | :deploy_token | false | :unauthorized
:developer | :job_token | true | :not_found
:developer | :job_token | false | :unauthorized
end
with_them do
before do
project.send("add_#{user_role}", user) unless user_role == :anonymous
end
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
it_behaves_like 'returning response status', params[:status]
end
end
shared_examples 'an unimplemented route' do
it_behaves_like 'without authentication'
it_behaves_like 'with authentication'
it_behaves_like 'when feature flag is disabled'
it_behaves_like 'when package feature is disabled'
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/:filename' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/specs.4.8.gz") }
subject { get(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/quick/Marshal.4.8/:file_name' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/quick/Marshal.4.8/my_gem-1.0.0.gemspec.rz") }
subject { get(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/gems/:file_name' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/gems/my_gem-1.0.0.gem") }
subject { get(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
describe 'POST /api/v4/projects/:project_id/packages/rubygems/api/v1/gems/authorize' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/api/v1/gems/authorize") }
subject { post(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
describe 'POST /api/v4/projects/:project_id/packages/rubygems/api/v1/gems' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/api/v1/gems") }
subject { post(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/api/v1/dependencies' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/api/v1/dependencies") }
subject { get(url, headers: headers) }
it_behaves_like 'an unimplemented route'
end
end

0
vendor/gitignore/C++.gitignore vendored Executable file → Normal file
View File

0
vendor/gitignore/Java.gitignore vendored Executable file → Normal file
View File