Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-13 12:08:29 +00:00
parent 8dae4070d2
commit cdd5eba514
39 changed files with 736 additions and 159 deletions

View File

@ -485,7 +485,7 @@ gem 'ssh_data', '~> 1.2'
gem 'spamcheck', '~> 0.1.0' gem 'spamcheck', '~> 0.1.0'
# Gitaly GRPC protocol definitions # Gitaly GRPC protocol definitions
gem 'gitaly', '~> 15.0.0-rc3' gem 'gitaly', '~> 15.1.0-rc1'
# KAS GRPC protocol definitions # KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2' gem 'kas-grpc', '~> 0.0.2'

View File

@ -466,7 +466,7 @@ GEM
rails (>= 3.2.0) rails (>= 3.2.0)
git (1.7.0) git (1.7.0)
rchardet (~> 1.8) rchardet (~> 1.8)
gitaly (15.0.0.pre.rc3) gitaly (15.1.0.pre.rc1)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab (4.16.1) gitlab (4.16.1)
@ -1529,7 +1529,7 @@ DEPENDENCIES
gettext (~> 3.3) gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly (~> 15.0.0.pre.rc3) gitaly (~> 15.1.0.pre.rc1)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5) gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 3.1.0) gitlab-dangerfiles (~> 3.1.0)

View File

@ -60,7 +60,7 @@ export default {
}, },
modalCloseButton: { modalCloseButton: {
text: __('Close'), text: __('Close'),
attributes: [{ variant: 'info' }], attributes: [{ variant: 'confirm' }],
}, },
}; };
</script> </script>

View File

@ -130,11 +130,7 @@ export default {
<slot name="extra-description"></slot> <slot name="extra-description"></slot>
</div> </div>
<div class="col-lg-9"> <div class="col-lg-9">
<gl-breadcrumb v-if="breadcrumbs" :items="breadcrumbs"> <gl-breadcrumb v-if="breadcrumbs" :items="breadcrumbs" />
<template #separator>
<gl-icon name="chevron-right" :size="8" />
</template>
</gl-breadcrumb>
<legacy-container :key="activePanel.name" :selector="activePanel.selector" /> <legacy-container :key="activePanel.name" :selector="activePanel.selector" />
</div> </div>
</div> </div>

View File

@ -2,12 +2,6 @@
module Diffs module Diffs
class OverflowWarningComponent < BaseComponent class OverflowWarningComponent < BaseComponent
# Skipping coverage because of https://gitlab.com/gitlab-org/gitlab/-/issues/357381
#
# This is fully tested by the output in the view part of this component,
# but undercoverage doesn't understand the relationship between the two parts.
#
# :nocov:
def initialize(diffs:, diff_files:, project:, commit: nil, merge_request: nil) def initialize(diffs:, diff_files:, project:, commit: nil, merge_request: nil)
@diffs = diffs @diffs = diffs
@diff_files = diff_files @diff_files = diff_files
@ -68,6 +62,5 @@ module Diffs
def button_classes def button_classes
"btn gl-alert-action btn-default gl-button btn-default-secondary" "btn gl-alert-action btn-default gl-button btn-default-secondary"
end end
# :nocov:
end end
end end

View File

@ -28,13 +28,6 @@ module Diffs
Gitlab::Json.dump(diffs_map) Gitlab::Json.dump(diffs_map)
end end
# Disabled undercoverage reports for this method
# as it returns a false positive on the last line,
# which is covered in the tests
#
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/357381
#
# :nocov:
def diff_file_path_text(diff_file, max: 60) def diff_file_path_text(diff_file, max: 60)
path = diff_file.new_path path = diff_file.new_path
@ -42,7 +35,6 @@ module Diffs
"...#{path[-(max - 3)..]}" "...#{path[-(max - 3)..]}"
end end
# :nocov:
private private

View File

@ -4,8 +4,6 @@ module Pajamas
class Component < ViewComponent::Base class Component < ViewComponent::Base
private private
# :nocov:
# Filter a given a value against a list of allowed values # Filter a given a value against a list of allowed values
# If no value is given or value is not allowed return default one # If no value is given or value is not allowed return default one
# #
@ -18,6 +16,5 @@ module Pajamas
default default
end end
# :nocov:
end end
end end

View File

@ -0,0 +1,60 @@
# frozen_string_literal: true
module Resolvers
class WorkItemsResolver < BaseResolver
include SearchArguments
include LooksAhead
type Types::WorkItemType.connection_type, null: true
argument :iid, GraphQL::Types::String,
required: false,
description: 'IID of the issue. For example, "1".'
argument :iids, [GraphQL::Types::String],
required: false,
description: 'List of IIDs of work items. For example, `["1", "2"]`.'
argument :sort, Types::WorkItemSortEnum,
description: 'Sort work items by this criteria.',
required: false,
default_value: :created_desc
argument :state, Types::IssuableStateEnum,
required: false,
description: 'Current state of this work item.'
argument :types, [Types::IssueTypeEnum],
as: :issue_types,
description: 'Filter work items by the given work item types.',
required: false
def resolve_with_lookahead(**args)
# The project could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` of the project to query for issues, so
# make sure it's loaded and not `nil` before continuing.
parent = object.respond_to?(:sync) ? object.sync : object
return WorkItem.none if parent.nil? || !parent.work_items_feature_flag_enabled?
args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
args[:attempt_project_search_optimizations] = true if args[:search].present?
finder = ::WorkItems::WorkItemsFinder.new(current_user, args)
Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) }
end
def ready?(**args)
validate_anonymous_search_access! if args[:search].present?
super
end
private
def unconditional_includes
[
{
project: [:project_feature, :group]
},
:author
]
end
end
end

View File

@ -144,6 +144,14 @@ module Types
extras: [:lookahead], extras: [:lookahead],
resolver: Resolvers::IssuesResolver resolver: Resolvers::IssuesResolver
field :work_items,
Types::WorkItemType.connection_type,
null: true,
deprecated: { milestone: '15.1', reason: :alpha },
description: 'Work items of the project.',
extras: [:lookahead],
resolver: Resolvers::WorkItemsResolver
field :issue_status_counts, field :issue_status_counts,
Types::IssueStatusCountsType, Types::IssueStatusCountsType,
null: true, null: true,

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Types
class WorkItemSortEnum < SortEnum
graphql_name 'WorkItemSort'
description 'Values for sorting work items'
value 'TITLE_ASC', 'Title by ascending order.', value: :title_asc
value 'TITLE_DESC', 'Title by descending order.', value: :title_desc
end
end

View File

@ -12,7 +12,8 @@ module Integrations
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
unless method_defined?(arg) unless method_defined?(arg)
def #{arg} def #{arg}
data_fields.send('#{arg}') || (properties && properties['#{arg}']) value = data_fields.send('#{arg}')
value.nil? ? properties&.dig('#{arg}') : value
end end
end end

View File

@ -81,7 +81,7 @@ module Integrations
[ [
{ key: 'HARBOR_URL', value: url }, { key: 'HARBOR_URL', value: url },
{ key: 'HARBOR_PROJECT', value: project_name }, { key: 'HARBOR_PROJECT', value: project_name },
{ key: 'HARBOR_USERNAME', value: username }, { key: 'HARBOR_USERNAME', value: username.gsub(/^robot\$/, 'robot$$') },
{ key: 'HARBOR_PASSWORD', value: password, public: false, masked: true } { key: 'HARBOR_PASSWORD', value: password, public: false, masked: true }
] ]
end end

View File

@ -36,7 +36,7 @@ module Packages
sha256: file_sha256 sha256: file_sha256
) )
append_metadata_file(content: file_md5, file_name: MD5_FILE_NAME) append_metadata_file(content: file_md5, file_name: MD5_FILE_NAME) unless Gitlab::FIPS.enabled?
append_metadata_file(content: file_sha1, file_name: SHA1_FILE_NAME) append_metadata_file(content: file_sha1, file_name: SHA1_FILE_NAME)
append_metadata_file(content: file_sha256, file_name: SHA256_FILE_NAME) append_metadata_file(content: file_sha256, file_name: SHA256_FILE_NAME)
append_metadata_file(content: file_sha512, file_name: SHA512_FILE_NAME) append_metadata_file(content: file_sha512, file_name: SHA512_FILE_NAME)
@ -70,6 +70,8 @@ module Packages
end end
def digest_from(content, type) def digest_from(content, type)
return if type == :md5 && Gitlab::FIPS.enabled?
digest_class = case type digest_class = case type
when :md5 when :md5
Digest::MD5 Digest::MD5

View File

@ -31,6 +31,14 @@ class GitlabUploader < CarrierWave::Uploader::Base
def absolute_path(upload_record) def absolute_path(upload_record)
File.join(root, upload_record.path) File.join(root, upload_record.path)
end end
def version(*args, **kwargs, &block)
# CarrierWave uploaded file "versions" are not tracked in the uploads
# table. Because of this they won't get replicated to Geo secondaries.
# If we ever want to use versions, we need to fix that first. Also see
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1757.
raise "using CarrierWave alternate file version is not supported"
end
end end
storage_options Gitlab.config.uploads storage_options Gitlab.config.uploads

View File

@ -7,7 +7,7 @@
= _("A member of the abuse team will review your report as soon as possible.") = _("A member of the abuse team will review your report as soon as possible.")
%hr %hr
= form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f| = form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report) = form_errors(@abuse_report, pajamas_alert: true)
= f.hidden_field :user_id = f.hidden_field :user_id
.form-group.row .form-group.row

View File

@ -1,5 +1,5 @@
= gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit js-requires-input' }, authenticity_token: true do |f| = gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit js-requires-input' }, authenticity_token: true do |f|
= form_errors(@topic) = form_errors(@topic, pajamas_alert: true)
.form-group .form-group
= f.label :name do = f.label :name do

View File

@ -1,4 +1,4 @@
= form_errors(@group) = form_errors(@group, pajamas_alert: true)
= render 'shared/group_form', f: f, autofocus: true = render 'shared/group_form', f: f, autofocus: true
.row .row

View File

@ -3,7 +3,7 @@
%section.settings.no-animate#branch-rules{ class: ('expanded' if expanded) } %section.settings.no-animate#branch-rules{ class: ('expanded' if expanded) }
.settings-header .settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Branch rules') %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Branch rules')
%button.btn.gl-button.btn-default.js-settings-toggle = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand') = expanded ? _('Collapse') : _('Expand')
%p %p
= _('Define rules for who can push, merge, and the required approvals for each branch.') = _('Define rules for who can push, merge, and the required approvals for each branch.')

View File

@ -106,8 +106,7 @@
.sidebar-collapsed-icon{ data: { toggle: 'tooltip', placement: 'left', container: 'body', boundary: 'viewport' }, title: _('Move issue') } .sidebar-collapsed-icon{ data: { toggle: 'tooltip', placement: 'left', container: 'body', boundary: 'viewport' }, title: _('Move issue') }
= sprite_icon('long-arrow') = sprite_icon('long-arrow')
.dropdown.sidebar-move-issue-dropdown.hide-collapsed .dropdown.sidebar-move-issue-dropdown.hide-collapsed
%button.gl-button.btn.btn-default.btn-block.js-sidebar-dropdown-toggle.js-move-issue{ type: 'button', = render Pajamas::ButtonComponent.new(block: true, button_options: { class: 'js-sidebar-dropdown-toggle js-move-issue', data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_action: "click_button", track_value: "" } } ) do
data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_action: "click_button", track_value: "" } }
= _('Move issue') = _('Move issue')
.dropdown-menu.dropdown-menu-selectable.dropdown-extended-height .dropdown-menu.dropdown-menu-selectable.dropdown-extended-height
= dropdown_title(_('Move issue')) = dropdown_title(_('Move issue'))

View File

@ -4,7 +4,7 @@ group: Memory
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
# Change the expiration interval for application cache **(FREE SELF)** # Application cache interval **(FREE SELF)**
By default, GitLab caches application settings for 60 seconds. Occasionally, By default, GitLab caches application settings for 60 seconds. Occasionally,
you may need to increase that interval to have more delay between application you may need to increase that interval to have more delay between application
@ -14,6 +14,8 @@ We recommend you set this value to greater than `0` seconds. Setting it to `0`
causes the `application_settings` table to load for every request. This causes causes the `application_settings` table to load for every request. This causes
extra load for Redis and PostgreSQL. extra load for Redis and PostgreSQL.
## Change the expiration interval for application cache
To change the expiry value: To change the expiry value:
**For Omnibus installations** **For Omnibus installations**
@ -32,8 +34,6 @@ To change the expiry value:
gitlab-ctl restart gitlab-ctl restart
``` ```
---
**For installations from source** **For installations from source**
1. Edit `config/gitlab.yml`: 1. Edit `config/gitlab.yml`:

View File

@ -15806,6 +15806,31 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="projectworkitemtypestaskable"></a>`taskable` | [`Boolean`](#boolean) | If `true`, only taskable work item types will be returned. Argument is experimental and can be removed in the future without notice. | | <a id="projectworkitemtypestaskable"></a>`taskable` | [`Boolean`](#boolean) | If `true`, only taskable work item types will be returned. Argument is experimental and can be removed in the future without notice. |
##### `Project.workItems`
Work items of the project.
WARNING:
**Deprecated** in 15.1.
This feature is in Alpha, and can be removed or changed at any point.
Returns [`WorkItemConnection`](#workitemconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectworkitemsiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="projectworkitemsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. |
| <a id="projectworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="projectworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by this criteria. |
| <a id="projectworkitemsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of this work item. |
| <a id="projectworkitemstypes"></a>`types` | [`[IssueType!]`](#issuetype) | Filter work items by the given work item types. |
### `ProjectCiCdSetting` ### `ProjectCiCdSetting`
#### Fields #### Fields
@ -19887,6 +19912,23 @@ Weight ID wildcard values.
| <a id="weightwildcardidany"></a>`ANY` | Weight is assigned. | | <a id="weightwildcardidany"></a>`ANY` | Weight is assigned. |
| <a id="weightwildcardidnone"></a>`NONE` | No weight is assigned. | | <a id="weightwildcardidnone"></a>`NONE` | No weight is assigned. |
### `WorkItemSort`
Values for sorting work items.
| Value | Description |
| ----- | ----------- |
| <a id="workitemsortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
| <a id="workitemsortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
| <a id="workitemsorttitle_asc"></a>`TITLE_ASC` | Title by ascending order. |
| <a id="workitemsorttitle_desc"></a>`TITLE_DESC` | Title by descending order. |
| <a id="workitemsortupdated_asc"></a>`UPDATED_ASC` | Updated at ascending order. |
| <a id="workitemsortupdated_desc"></a>`UPDATED_DESC` | Updated at descending order. |
| <a id="workitemsortcreated_asc"></a>`created_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_ASC`. |
| <a id="workitemsortcreated_desc"></a>`created_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_DESC`. |
| <a id="workitemsortupdated_asc"></a>`updated_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_ASC`. |
| <a id="workitemsortupdated_desc"></a>`updated_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_DESC`. |
### `WorkItemState` ### `WorkItemState`
State of a GitLab work item. State of a GitLab work item.

View File

@ -113,8 +113,8 @@ the following table) as these were used for development and testing:
|----------------|----------------------------| |----------------|----------------------------|
| 13.0 | 11 | | 13.0 | 11 |
| 14.0 | 12.10 | | 14.0 | 12.10 |
| 15.0 | 12.0 | | 15.0 | 12.10 |
| 16.0 (planned) | 13.0 | | 16.0 (planned) | 13.6 |
You must also ensure the following extensions are loaded into every You must also ensure the following extensions are loaded into every
GitLab database. [Read more about this requirement, and troubleshooting](postgresql_extensions.md). GitLab database. [Read more about this requirement, and troubleshooting](postgresql_extensions.md).

View File

@ -18,7 +18,7 @@ this feature, ask an administrator to [enable the feature flag](../../../adminis
`bulk_import_projects`. On GitLab.com, migrating group resources is available but migrating project resources is not `bulk_import_projects`. On GitLab.com, migrating group resources is available but migrating project resources is not
available. available.
You can migrate your existing top-level groups to any of the following: Users with the Owner role on a top-level group can migrate it to:
- Another top-level group. - Another top-level group.
- The subgroup of any existing top-level group. - The subgroup of any existing top-level group.

View File

@ -431,7 +431,7 @@ The following table lists group permissions available for each role:
| View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlab-saas-subscription) | | | | | ✓ (4) | | View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlab-saas-subscription) | | | | | ✓ (4) |
| View group [Usage Quotas](usage_quotas.md) page | | | | | ✓ (4) | | View group [Usage Quotas](usage_quotas.md) page | | | | | ✓ (4) |
| Manage group runners | | | | | ✓ | | Manage group runners | | | | | ✓ |
| GitLab migration | | | | | ✓ | | [Migrate groups](group/import/index.md) | | | | | ✓ |
<!-- markdownlint-disable MD029 --> <!-- markdownlint-disable MD029 -->

View File

@ -39,7 +39,7 @@ GitLab supports integrating Harbor projects at the group or project level. Compl
After the Harbor integration is activated: After the Harbor integration is activated:
- The global variables `$HARBOR_USER`, `$HARBOR_PASSWORD`, `$HARBOR_URL`, and `$HARBOR_PROJECT` are created for CI/CD use. - The global variables `$HARBOR_USERNAME`, `$HARBOR_PASSWORD`, `$HARBOR_URL`, and `$HARBOR_PROJECT` are created for CI/CD use.
- The project-level integration settings override the group-level integration settings. - The project-level integration settings override the group-level integration settings.
## Secure your requests to the Harbor APIs ## Secure your requests to the Harbor APIs

View File

@ -101,6 +101,16 @@ module Gitlab
if pre_receive_error = response.pre_receive_error.presence if pre_receive_error = response.pre_receive_error.presence
raise Gitlab::Git::PreReceiveError, pre_receive_error raise Gitlab::Git::PreReceiveError, pre_receive_error
end end
rescue GRPC::BadStatus => e
detailed_error = decode_detailed_error(e)
case detailed_error&.error
when :custom_hook
raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
fallback_message: e.details)
else
raise
end
end end
def user_merge_to_ref(user, source_sha:, branch:, target_ref:, message:, first_parent_ref:, allow_conflicts: false) def user_merge_to_ref(user, source_sha:, branch:, target_ref:, message:, first_parent_ref:, allow_conflicts: false)
@ -164,14 +174,8 @@ module Gitlab
# These messages were returned from internal/allowed API calls # These messages were returned from internal/allowed API calls
raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message) raise Gitlab::Git::PreReceiveError.new(fallback_message: access_check_error.error_message)
when :custom_hook when :custom_hook
# Custom hooks may return messages via either stdout or stderr which have a specific prefix. If raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
# that prefix is present we'll want to print the hook's output, otherwise we'll want to print the fallback_message: e.details)
# Gitaly error as a fallback.
custom_hook_error = detailed_error.custom_hook
custom_hook_output = custom_hook_error.stderr.presence || custom_hook_error.stdout
error_message = EncodingHelper.encode!(custom_hook_output)
raise Gitlab::Git::PreReceiveError.new(error_message, fallback_message: e.details)
when :reference_update when :reference_update
# We simply ignore any reference update errors which are typically an # We simply ignore any reference update errors which are typically an
# indicator of multiple RPC calls trying to update the same reference # indicator of multiple RPC calls trying to update the same reference
@ -308,10 +312,6 @@ module Gitlab
timeout: GitalyClient.long_timeout timeout: GitalyClient.long_timeout
) )
if response.git_error.presence
raise Gitlab::Git::Repository::GitError, response.git_error
end
response.squash_sha response.squash_sha
rescue GRPC::BadStatus => e rescue GRPC::BadStatus => e
detailed_error = decode_detailed_error(e) detailed_error = decode_detailed_error(e)
@ -550,6 +550,14 @@ module Gitlab
# Error Class might not be known to ruby yet # Error Class might not be known to ruby yet
nil nil
end end
def custom_hook_error_message(custom_hook_error)
# Custom hooks may return messages via either stdout or stderr which have a specific prefix. If
# that prefix is present we'll want to print the hook's output, otherwise we'll want to print the
# Gitaly error as a fallback.
custom_hook_output = custom_hook_error.stderr.presence || custom_hook_error.stdout
EncodingHelper.encode!(custom_hook_output)
end
end end
end end
end end

View File

@ -207,7 +207,7 @@
"@babel/plugin-transform-modules-commonjs": "^7.10.1", "@babel/plugin-transform-modules-commonjs": "^7.10.1",
"@gitlab/eslint-plugin": "12.1.0", "@gitlab/eslint-plugin": "12.1.0",
"@gitlab/stylelint-config": "4.0.0", "@gitlab/stylelint-config": "4.0.0",
"@graphql-eslint/eslint-plugin": "3.10.3", "@graphql-eslint/eslint-plugin": "3.10.4",
"@testing-library/dom": "^7.16.2", "@testing-library/dom": "^7.16.2",
"@types/jest": "^26.0.24", "@types/jest": "^26.0.24",
"@vue/test-utils": "1.3.0", "@vue/test-utils": "1.3.0",

View File

@ -0,0 +1,190 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::WorkItemsResolver do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:other_project) { create(:project, group: group) }
let_it_be(:item1) do
create(:work_item, project: project, state: :opened, created_at:
3.hours.ago, updated_at: 3.hours.ago)
end
let_it_be(:item2) do
create(:work_item, project: project, state: :closed, title: 'foo',
created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
1.hour.ago)
end
let_it_be(:item3) do
create(:work_item, project: other_project, state: :closed, title: 'foo',
created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
1.hour.ago)
end
let_it_be(:item4) { create(:work_item) }
specify do
expect(described_class).to have_nullable_graphql_type(Types::WorkItemType.connection_type)
end
context "with a project" do
before_all do
project.add_developer(current_user)
project.add_reporter(reporter)
end
describe '#resolve' do
it 'finds all items' do
expect(resolve_items).to contain_exactly(item1, item2)
end
it 'filters by state' do
expect(resolve_items(state: 'opened')).to contain_exactly(item1)
expect(resolve_items(state: 'closed')).to contain_exactly(item2)
end
context 'when searching items' do
it 'returns correct items' do
expect(resolve_items(search: 'foo')).to contain_exactly(item2)
end
it 'uses project search optimization' do
expected_arguments = a_hash_including(
search: 'foo',
attempt_project_search_optimizations: true
)
expect(::WorkItems::WorkItemsFinder).to receive(:new).with(anything, expected_arguments).and_call_original
resolve_items(search: 'foo')
end
context 'with anonymous user' do
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:public_item) { create(:work_item, project: public_project, title: 'Test item') }
context 'with disable_anonymous_search enabled' do
before do
stub_feature_flags(disable_anonymous_search: true)
end
it 'generates an error' do
error_message = "User must be authenticated to include the `search` argument."
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
end
end
end
context 'with disable_anonymous_search disabled' do
before do
stub_feature_flags(disable_anonymous_search: false)
end
it 'returns correct items' do
expect(
resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
).to contain_exactly(public_item)
end
end
end
end
describe 'sorting' do
context 'when sorting by created' do
it 'sorts items ascending' do
expect(resolve_items(sort: 'created_asc').to_a).to eq [item1, item2]
end
it 'sorts items descending' do
expect(resolve_items(sort: 'created_desc').to_a).to eq [item2, item1]
end
end
context 'when sorting by title' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:item1) { create(:work_item, project: project, title: 'foo') }
let_it_be(:item2) { create(:work_item, project: project, title: 'bar') }
let_it_be(:item3) { create(:work_item, project: project, title: 'baz') }
let_it_be(:item4) { create(:work_item, project: project, title: 'Baz 2') }
it 'sorts items ascending' do
expect(resolve_items(sort: :title_asc).to_a).to eq [item2, item3, item4, item1]
end
it 'sorts items descending' do
expect(resolve_items(sort: :title_desc).to_a).to eq [item1, item4, item3, item2]
end
end
end
it 'returns items user can see' do
project.add_guest(current_user)
create(:work_item, confidential: true)
expect(resolve_items).to contain_exactly(item1, item2)
end
it 'batches queries that only include IIDs', :request_store do
result = batch_sync(max_queries: 7) do
[item1, item2]
.map { |item| resolve_items(iid: item.iid.to_s) }
.flat_map(&:to_a)
end
expect(result).to contain_exactly(item1, item2)
end
it 'finds a specific item with iids', :request_store do
result = batch_sync(max_queries: 7) do
resolve_items(iids: [item1.iid]).to_a
end
expect(result).to contain_exactly(item1)
end
it 'finds multiple items with iids' do
create(:work_item, project: project, author: current_user)
expect(batch_sync { resolve_items(iids: [item1.iid, item2.iid]).to_a })
.to contain_exactly(item1, item2)
end
it 'finds only the items within the project we are looking at' do
another_project = create(:project)
iids = [item1, item2].map(&:iid)
iids.each do |iid|
create(:work_item, project: another_project, iid: iid)
end
expect(batch_sync { resolve_items(iids: iids).to_a }).to contain_exactly(item1, item2)
end
end
end
context "when passing a non existent, batch loaded project" do
let!(:project) do
BatchLoader::GraphQL.for("non-existent-path").batch do |_fake_paths, loader, _|
loader.call("non-existent-path", nil)
end
end
it "returns nil without breaking" do
expect(resolve_items(iids: ["don't", "break"])).to be_empty
end
end
def resolve_items(args = {}, context = { current_user: current_user })
resolve(described_class, obj: project, args: args, ctx: context)
end
end

View File

@ -170,6 +170,65 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
Gitlab::Git::PreReceiveError, "something failed") Gitlab::Git::PreReceiveError, "something failed")
end end
end end
context 'with a custom hook error' do
let(:stdout) { nil }
let(:stderr) { nil }
let(:error_message) { "error_message" }
let(:custom_hook_error) do
new_detailed_error(
GRPC::Core::StatusCodes::PERMISSION_DENIED,
error_message,
Gitaly::UserDeleteBranchError.new(
custom_hook: Gitaly::CustomHookError.new(
stdout: stdout,
stderr: stderr,
hook_type: Gitaly::CustomHookError::HookType::HOOK_TYPE_PRERECEIVE
)))
end
shared_examples 'a failed branch deletion' do
it 'raises a PreRecieveError' do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_delete_branch).with(request, kind_of(Hash))
.and_raise(custom_hook_error)
expect { subject }.to raise_error do |error|
expect(error).to be_a(Gitlab::Git::PreReceiveError)
expect(error.message).to eq(expected_message)
expect(error.raw_message).to eq(expected_raw_message)
end
end
end
context 'when details contain stderr' do
let(:stderr) { "something" }
let(:stdout) { "GL-HOOK-ERR: stdout is overridden by stderr" }
let(:expected_message) { error_message }
let(:expected_raw_message) { stderr }
it_behaves_like 'a failed branch deletion'
end
context 'when details contain stdout' do
let(:stderr) { " \n" }
let(:stdout) { "something" }
let(:expected_message) { error_message }
let(:expected_raw_message) { stdout }
it_behaves_like 'a failed branch deletion'
end
end
context 'with a non-detailed error' do
it 'raises a GRPC error' do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_delete_branch).with(request, kind_of(Hash))
.and_raise(GRPC::Internal.new('non-detailed error'))
expect { subject }.to raise_error(GRPC::Internal)
end
end
end end
describe '#user_merge_branch' do describe '#user_merge_branch' do
@ -635,21 +694,6 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
expect(subject).to eq(squash_sha) expect(subject).to eq(squash_sha)
end end
context "when git_error is present" do
let(:response) do
Gitaly::UserSquashResponse.new(git_error: "something failed")
end
it "raises a GitError exception" do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_squash).with(request, kind_of(Hash))
.and_return(response)
expect { subject }.to raise_error(
Gitlab::Git::Repository::GitError, "something failed")
end
end
shared_examples '#user_squash with an error' do shared_examples '#user_squash with an error' do
it 'raises a GitError exception' do it 'raises a GitError exception' do
expect_any_instance_of(Gitaly::OperationService::Stub) expect_any_instance_of(Gitaly::OperationService::Stub)

View File

@ -6,84 +6,84 @@ RSpec.describe Integrations::HasDataFields do
let(:url) { 'http://url.com' } let(:url) { 'http://url.com' }
let(:username) { 'username_one' } let(:username) { 'username_one' }
let(:properties) do let(:properties) do
{ url: url, username: username } { url: url, username: username, jira_issue_transition_automatic: false }
end end
shared_examples 'data fields' do shared_examples 'data fields' do
describe '#arg' do describe '#arg' do
it 'returns an argument correctly' do it 'returns the expected values' do
expect(service.url).to eq(url) expect(integration).to have_attributes(properties)
end end
end end
describe '{arg}_changed?' do describe '{arg}_changed?' do
it 'returns false when the property has not been assigned a new value' do it 'returns false when the property has not been assigned a new value' do
service.username = 'new_username' integration.username = 'new_username'
service.validate integration.validate
expect(service.url_changed?).to be_falsy expect(integration.url_changed?).to be_falsy
end end
it 'returns true when the property has been assigned a different value' do it 'returns true when the property has been assigned a different value' do
service.url = "http://example.com" integration.url = "http://example.com"
service.validate integration.validate
expect(service.url_changed?).to be_truthy expect(integration.url_changed?).to be_truthy
end end
it 'returns true when the property has been assigned a different value twice' do it 'returns true when the property has been assigned a different value twice' do
service.url = "http://example.com" integration.url = "http://example.com"
service.url = "http://example.com" integration.url = "http://example.com"
service.validate integration.validate
expect(service.url_changed?).to be_truthy expect(integration.url_changed?).to be_truthy
end end
it 'returns false when the property has been re-assigned the same value' do it 'returns false when the property has been re-assigned the same value' do
service.url = 'http://url.com' integration.url = 'http://url.com'
service.validate integration.validate
expect(service.url_changed?).to be_falsy expect(integration.url_changed?).to be_falsy
end end
end end
describe '{arg}_touched?' do describe '{arg}_touched?' do
it 'returns false when the property has not been assigned a new value' do it 'returns false when the property has not been assigned a new value' do
service.username = 'new_username' integration.username = 'new_username'
service.validate integration.validate
expect(service.url_changed?).to be_falsy expect(integration.url_changed?).to be_falsy
end end
it 'returns true when the property has been assigned a different value' do it 'returns true when the property has been assigned a different value' do
service.url = "http://example.com" integration.url = "http://example.com"
service.validate integration.validate
expect(service.url_changed?).to be_truthy expect(integration.url_changed?).to be_truthy
end end
it 'returns true when the property has been assigned a different value twice' do it 'returns true when the property has been assigned a different value twice' do
service.url = "http://example.com" integration.url = "http://example.com"
service.url = "http://example.com" integration.url = "http://example.com"
service.validate integration.validate
expect(service.url_changed?).to be_truthy expect(integration.url_changed?).to be_truthy
end end
it 'returns true when the property has been re-assigned the same value' do it 'returns true when the property has been re-assigned the same value' do
service.url = 'http://url.com' integration.url = 'http://url.com'
expect(service.url_touched?).to be_truthy expect(integration.url_touched?).to be_truthy
end end
it 'returns false when the property has been re-assigned the same value' do it 'returns false when the property has been re-assigned the same value' do
service.url = 'http://url.com' integration.url = 'http://url.com'
service.validate integration.validate
expect(service.url_changed?).to be_falsy expect(integration.url_changed?).to be_falsy
end end
end end
describe 'data_fields_present?' do describe 'data_fields_present?' do
it 'returns true from the issue tracker service' do it 'returns true from the issue tracker integration' do
expect(service.data_fields_present?).to be true expect(integration.data_fields_present?).to be true
end end
end end
end end
context 'when data are stored in data_fields' do context 'when data are stored in data_fields' do
let(:service) do let(:integration) do
create(:jira_integration, url: url, username: username) create(:jira_integration, url: url, username: username)
end end
@ -91,21 +91,21 @@ RSpec.describe Integrations::HasDataFields do
describe '{arg}_was?' do describe '{arg}_was?' do
it 'returns nil' do it 'returns nil' do
service.url = 'http://example.com' integration.url = 'http://example.com'
service.validate integration.validate
expect(service.url_was).to be_nil expect(integration.url_was).to be_nil
end end
end end
end end
context 'when service and data_fields are not persisted' do context 'when integration and data_fields are not persisted' do
let(:service) do let(:integration) do
Integrations::Jira.new Integrations::Jira.new
end end
describe 'data_fields_present?' do describe 'data_fields_present?' do
it 'returns true' do it 'returns true' do
expect(service.data_fields_present?).to be true expect(integration.data_fields_present?).to be true
end end
end end
end end
@ -113,9 +113,7 @@ RSpec.describe Integrations::HasDataFields do
context 'when data are stored in properties' do context 'when data are stored in properties' do
let(:integration) { create(:jira_integration, :without_properties_callback, properties: properties) } let(:integration) { create(:jira_integration, :without_properties_callback, properties: properties) }
it_behaves_like 'data fields' do it_behaves_like 'data fields'
let(:service) { integration }
end
describe '{arg}_was?' do describe '{arg}_was?' do
it 'returns nil when the property has not been assigned a new value' do it 'returns nil when the property has not been assigned a new value' do
@ -148,9 +146,7 @@ RSpec.describe Integrations::HasDataFields do
end end
end end
it_behaves_like 'data fields' do it_behaves_like 'data fields'
let(:service) { integration }
end
describe '{arg}_was?' do describe '{arg}_was?' do
it 'returns nil' do it 'returns nil' do

View File

@ -67,6 +67,16 @@ RSpec.describe Integrations::Harbor do
harbor_integration.update!(active: false) harbor_integration.update!(active: false)
expect(harbor_integration.ci_variables).to match_array([]) expect(harbor_integration.ci_variables).to match_array([])
end end
context 'with robot username' do
it 'returns username variable with $$' do
harbor_integration.username = 'robot$project+user'
expect(harbor_integration.ci_variables).to include(
{ key: 'HARBOR_USERNAME', value: 'robot$$project+user' }
)
end
end
end end
describe 'before_validation :reset_username_and_password' do describe 'before_validation :reset_username_and_password' do

View File

@ -0,0 +1,121 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting an work item list for a project' do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, :public, group: group) }
let_it_be(:current_user) { create(:user) }
let_it_be(:item1) { create(:work_item, project: project, discussion_locked: true, title: 'item1') }
let_it_be(:item2) { create(:work_item, project: project, title: 'item2') }
let_it_be(:confidential_item) { create(:work_item, confidential: true, project: project, title: 'item3') }
let_it_be(:other_item) { create(:work_item) }
let(:items_data) { graphql_data['project']['workItems']['edges'] }
let(:item_filter_params) { {} }
let(:fields) do
<<~QUERY
edges {
node {
#{all_graphql_fields_for('workItems'.classify)}
}
}
QUERY
end
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('workItems', item_filter_params, fields)
)
end
it_behaves_like 'a working graphql query' do
before do
post_graphql(query, current_user: current_user)
end
end
context 'when the user does not have access to the item' do
before do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
end
it 'returns an empty list' do
post_graphql(query)
expect(items_data).to eq([])
end
end
context 'when work_items flag is disabled' do
before do
stub_feature_flags(work_items: false)
end
it 'returns an empty list' do
post_graphql(query)
expect(items_data).to eq([])
end
end
it 'returns only items visible to user' do
post_graphql(query, current_user: current_user)
expect(item_ids).to eq([item2.to_global_id.to_s, item1.to_global_id.to_s])
end
context 'when the user can see confidential items' do
before do
project.add_developer(current_user)
end
it 'returns also confidential items' do
post_graphql(query, current_user: current_user)
expect(item_ids).to eq([confidential_item.to_global_id.to_s, item2.to_global_id.to_s, item1.to_global_id.to_s])
end
end
describe 'sorting and pagination' do
let(:data_path) { [:project, :work_items] }
def pagination_query(params)
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('workItems', params, "#{page_info} nodes { id }")
)
end
before do
project.add_developer(current_user)
end
context 'when sorting by title ascending' do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { :TITLE_ASC }
let(:first_param) { 2 }
let(:all_records) { [item1, item2, confidential_item].map { |item| item.to_global_id.to_s } }
end
end
context 'when sorting by title descending' do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { :TITLE_DESC }
let(:first_param) { 2 }
let(:all_records) { [confidential_item, item2, item1].map { |item| item.to_global_id.to_s } }
end
end
end
def item_ids
graphql_dig_at(items_data, :node, :id)
end
end

View File

@ -55,33 +55,49 @@ RSpec.describe API::Integrations do
describe "PUT /projects/:id/#{endpoint}/#{integration.dasherize}" do describe "PUT /projects/:id/#{endpoint}/#{integration.dasherize}" do
include_context integration include_context integration
# NOTE: Some attributes are not supported for PUT requests, even though in most cases they should be.
# For some of them the problem is somewhere else, i.e. most chat integrations don't support the `*_channel`
# fields but they're incorrectly included in `#fields`.
#
# We can fix these manually, or with a generic approach like https://gitlab.com/gitlab-org/gitlab/-/issues/348208
let(:missing_channel_attributes) { %i[push_channel issue_channel confidential_issue_channel merge_request_channel note_channel confidential_note_channel tag_push_channel pipeline_channel wiki_page_channel] }
let(:missing_attributes) do
{
datadog: %i[archive_trace_events],
discord: missing_channel_attributes + %i[branches_to_be_notified notify_only_broken_pipelines],
hangouts_chat: missing_channel_attributes + %i[notify_only_broken_pipelines],
jira: %i[issues_enabled project_key vulnerabilities_enabled vulnerabilities_issuetype],
mattermost: %i[deployment_channel labels_to_be_notified],
microsoft_teams: missing_channel_attributes,
mock_ci: %i[enable_ssl_verification],
prometheus: %i[manual_configuration],
slack: %i[alert_events alert_channel deployment_channel labels_to_be_notified],
unify_circuit: missing_channel_attributes + %i[branches_to_be_notified notify_only_broken_pipelines],
webex_teams: missing_channel_attributes + %i[branches_to_be_notified notify_only_broken_pipelines]
}
end
it "updates #{integration} settings and returns the correct fields" do it "updates #{integration} settings and returns the correct fields" do
put api("/projects/#{project.id}/#{endpoint}/#{dashed_integration}", user), params: integration_attrs supported_attrs = integration_attrs.without(missing_attributes.fetch(integration.to_sym, []))
expect(response).to have_gitlab_http_status(:ok) put api("/projects/#{project.id}/#{endpoint}/#{dashed_integration}", user), params: supported_attrs
current_integration = project.integrations.first
events = current_integration.event_names.empty? ? ["foo"].freeze : current_integration.event_names
query_strings = []
events.map(&:to_sym).each do |event|
event_value = !current_integration[event]
query_strings << "#{event}=#{event_value}"
integration_attrs[event] = event_value if integration_attrs[event].present?
end
query_strings = query_strings.join('&')
put api("/projects/#{project.id}/#{endpoint}/#{dashed_integration}?#{query_strings}", user), params: integration_attrs
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['slug']).to eq(dashed_integration) expect(json_response['slug']).to eq(dashed_integration)
events.each do |event|
next if event == "foo"
expect(project.integrations.first[event]).not_to eq(current_integration[event]), current_integration = project.integrations.first
"expected #{!current_integration[event]} for event #{event} for #{endpoint} #{current_integration.title}, got #{current_integration[event]}" expect(current_integration).to have_attributes(supported_attrs)
assert_correct_response_fields(json_response['properties'].keys, current_integration)
# Flip all booleans and verify that we can set these too
flipped_attrs = supported_attrs.transform_values do |value|
[true, false].include?(value) ? !value : value
end end
assert_correct_response_fields(json_response['properties'].keys, current_integration) put api("/projects/#{project.id}/#{endpoint}/#{dashed_integration}", user), params: flipped_attrs
expect(response).to have_gitlab_http_status(:ok)
expect(project.integrations.first).to have_attributes(flipped_attrs)
end end
it "returns if required fields missing" do it "returns if required fields missing" do

View File

@ -22,6 +22,18 @@ RSpec.describe ::Packages::Maven::Metadata::AppendPackageFileService do
expect_file("#{metadata_file_name}.sha256") expect_file("#{metadata_file_name}.sha256")
expect_file("#{metadata_file_name}.sha512") expect_file("#{metadata_file_name}.sha512")
end end
context 'with FIPS mode', :fips_mode do
it 'does not generate file_md5' do
expect { subject }.to change { package.package_files.count }.by(4)
expect(subject).to be_success
expect_file(metadata_file_name, with_content: content, with_content_type: 'application/xml', fips: true)
expect_file("#{metadata_file_name}.sha1", fips: true)
expect_file("#{metadata_file_name}.sha256", fips: true)
expect_file("#{metadata_file_name}.sha512", fips: true)
end
end
end end
context 'with nil content' do context 'with nil content' do
@ -36,17 +48,22 @@ RSpec.describe ::Packages::Maven::Metadata::AppendPackageFileService do
it_behaves_like 'returning an error service response', message: 'package is not set' it_behaves_like 'returning an error service response', message: 'package is not set'
end end
def expect_file(file_name, with_content: nil, with_content_type: '') def expect_file(file_name, fips: false, with_content: nil, with_content_type: '')
package_file = package.package_files.recent.with_file_name(file_name).first package_file = package.package_files.recent.with_file_name(file_name).first
expect(package_file.file).to be_present expect(package_file.file).to be_present
expect(package_file.file_name).to eq(file_name) expect(package_file.file_name).to eq(file_name)
expect(package_file.size).to be > 0 expect(package_file.size).to be > 0
expect(package_file.file_md5).to be_present
expect(package_file.file_sha1).to be_present expect(package_file.file_sha1).to be_present
expect(package_file.file_sha256).to be_present expect(package_file.file_sha256).to be_present
expect(package_file.file.content_type).to eq(with_content_type) expect(package_file.file.content_type).to eq(with_content_type)
if fips
expect(package_file.file_md5).not_to be_present
else
expect(package_file.file_md5).to be_present
end
if with_content if with_content
expect(package_file.file.read).to eq(with_content) expect(package_file.file.read).to eq(with_content)
end end

View File

@ -8,8 +8,36 @@ Integration.available_integration_names.each do |integration|
let(:integration_method) { Project.integration_association_name(integration) } let(:integration_method) { Project.integration_association_name(integration) }
let(:integration_klass) { Integration.integration_name_to_model(integration) } let(:integration_klass) { Integration.integration_name_to_model(integration) }
let(:integration_instance) { integration_klass.new } let(:integration_instance) { integration_klass.new }
let(:integration_fields) { integration_instance.fields }
let(:integration_attrs_list) { integration_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } # Build a list of all attributes that an integration supports.
let(:integration_attrs_list) do
integration_fields + integration_events + custom_attributes.fetch(integration.to_sym, [])
end
# Attributes defined as fields.
let(:integration_fields) do
integration_instance.fields.map { _1[:name].to_sym }
end
# Attributes for configurable event triggers.
let(:integration_events) do
integration_instance.configurable_events.map { IntegrationsHelper.integration_event_field_name(_1).to_sym }
end
# Other special cases, this list might be incomplete.
#
# Some of these won't be needed anymore after we've converted them to use the field DSL
# in https://gitlab.com/gitlab-org/gitlab/-/issues/354899.
#
# Others like `comment_on_event_disabled` are actual columns on `integrations`, maybe we should migrate
# these to fields as well.
let(:custom_attributes) do
{
jira: %i[comment_on_event_enabled jira_issue_transition_automatic jira_issue_transition_id project_key
issues_enabled vulnerabilities_enabled vulnerabilities_issuetype]
}
end
let(:integration_attrs) do let(:integration_attrs) do
integration_attrs_list.inject({}) do |hash, k| integration_attrs_list.inject({}) do |hash, k|
if k =~ /^(token*|.*_token|.*_key)/ if k =~ /^(token*|.*_token|.*_key)/
@ -32,9 +60,11 @@ Integration.available_integration_names.each do |integration|
hash.merge!(k => 1234) hash.merge!(k => 1234)
elsif integration == 'jira' && k == :jira_issue_transition_id elsif integration == 'jira' && k == :jira_issue_transition_id
hash.merge!(k => '1,2,3') hash.merge!(k => '1,2,3')
elsif integration == 'jira' && k == :jira_issue_transition_automatic
hash.merge!(k => true)
elsif integration == 'emails_on_push' && k == :recipients elsif integration == 'emails_on_push' && k == :recipients
hash.merge!(k => 'foo@bar.com') hash.merge!(k => 'foo@bar.com')
elsif integration == 'slack' || integration == 'mattermost' && k == :labels_to_be_notified_behavior elsif (integration == 'slack' || integration == 'mattermost') && k == :labels_to_be_notified_behavior
hash.merge!(k => "match_any") hash.merge!(k => "match_any")
else else
hash.merge!(k => "someword") hash.merge!(k => "someword")

View File

@ -1,8 +1,33 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper'
require_relative '../../../tooling/quality/test_level' require_relative '../../../tooling/quality/test_level'
RSpec.describe Quality::TestLevel do RSpec.describe Quality::TestLevel do
describe 'TEST_LEVEL_FOLDERS constant' do
it 'all directories it refers to exists', :aggregate_failures do
ee_only_directories = %w[
lib/ee/gitlab/background_migration
elastic
elastic_integration
replicators
]
described_class::TEST_LEVEL_FOLDERS.values.flatten.each do |dir|
next if ee_only_directories.include?(dir) && !Gitlab.ee?
spec_directory = if ee_only_directories.include?(dir)
File.join('ee', 'spec', dir)
else
File.join('spec', dir)
end
expect(File.exist?(spec_directory)).to eq(true), "#{spec_directory} does not exist!"
end
end
end
describe '#pattern' do describe '#pattern' do
context 'when level is all' do context 'when level is all' do
it 'returns a pattern' do it 'returns a pattern' do
@ -21,7 +46,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do context 'when level is unit' do
it 'returns a pattern' do it 'returns a pattern' do
expect(subject.pattern(:unit)) expect(subject.pattern(:unit))
.to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,events,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,component}{,/**/}*_spec.rb") .to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,events,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,components}{,/**/}*_spec.rb")
end end
end end
@ -89,56 +114,56 @@ RSpec.describe Quality::TestLevel do
context 'when level is frontend_fixture' do context 'when level is frontend_fixture' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:frontend_fixture)) expect(subject.regexp(:frontend_fixture))
.to eq(%r{spec/(frontend/fixtures)}) .to eq(%r{spec/(frontend/fixtures)/})
end end
end end
context 'when level is unit' do context 'when level is unit' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:unit)) expect(subject.regexp(:unit))
.to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|events|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|component)}) .to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|events|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|components)/})
end end
end end
context 'when level is migration' do context 'when level is migration' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:migration)) expect(subject.regexp(:migration))
.to eq(%r{spec/(migrations|lib/gitlab/background_migration|lib/ee/gitlab/background_migration)}) .to eq(%r{spec/(migrations|lib/gitlab/background_migration|lib/ee/gitlab/background_migration)/})
end end
end end
context 'when level is background_migration' do context 'when level is background_migration' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:background_migration)) expect(subject.regexp(:background_migration))
.to eq(%r{spec/(lib/gitlab/background_migration|lib/ee/gitlab/background_migration)}) .to eq(%r{spec/(lib/gitlab/background_migration|lib/ee/gitlab/background_migration)/})
end end
end end
context 'when level is integration' do context 'when level is integration' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:integration)) expect(subject.regexp(:integration))
.to eq(%r{spec/(commands|controllers|mailers|requests)}) .to eq(%r{spec/(commands|controllers|mailers|requests)/})
end end
end end
context 'when level is system' do context 'when level is system' do
it 'returns a regexp' do it 'returns a regexp' do
expect(subject.regexp(:system)) expect(subject.regexp(:system))
.to eq(%r{spec/(features)}) .to eq(%r{spec/(features)/})
end end
end end
context 'with a prefix' do context 'with a prefix' do
it 'returns a regexp' do it 'returns a regexp' do
expect(described_class.new('ee/').regexp(:system)) expect(described_class.new('ee/').regexp(:system))
.to eq(%r{(ee/)spec/(features)}) .to eq(%r{(ee/)spec/(features)/})
end end
end end
context 'with several prefixes' do context 'with several prefixes' do
it 'returns a regexp' do it 'returns a regexp' do
expect(described_class.new(['', 'ee/', 'jh/']).regexp(:system)) expect(described_class.new(['', 'ee/', 'jh/']).regexp(:system))
.to eq(%r{(|ee/|jh/)spec/(features)}) .to eq(%r{(|ee/|jh/)spec/(features)/})
end end
end end

View File

@ -160,4 +160,10 @@ RSpec.describe GitlabUploader do
end end
end end
end end
describe '.version' do
subject { uploader_class.version }
it { expect { subject }.to raise_error(RuntimeError, /not supported/) }
end
end end

View File

@ -32,7 +32,6 @@ module Quality
haml_lint haml_lint
helpers helpers
initializers initializers
javascripts
lib lib
metrics_server metrics_server
models models
@ -55,7 +54,7 @@ module Quality
views views
workers workers
tooling tooling
component components
], ],
integration: %w[ integration: %w[
commands commands
@ -82,6 +81,10 @@ module Quality
@regexps[level] ||= Regexp.new("#{prefixes_for_regex}spec/#{folders_regex(level)}").freeze @regexps[level] ||= Regexp.new("#{prefixes_for_regex}spec/#{folders_regex(level)}").freeze
end end
def legacy_factories_regexp
@legacy_factories_regexp ||= %r{spec/factories_spec.rb}.freeze
end
def level_for(file_path) def level_for(file_path)
case file_path case file_path
# Detect migration first since some background migration tests are under # Detect migration first since some background migration tests are under
@ -97,6 +100,8 @@ module Quality
:integration :integration
when regexp(:system) when regexp(:system)
:system :system
when legacy_factories_regexp
:unit
else else
raise UnknownTestLevelError, "Test level for #{file_path} couldn't be set. Please rename the file properly or change the test level detection regexes in #{__FILE__}." raise UnknownTestLevelError, "Test level for #{file_path} couldn't be set. Please rename the file properly or change the test level detection regexes in #{__FILE__}."
end end
@ -149,11 +154,11 @@ module Quality
def folders_regex(level) def folders_regex(level)
case level case level
when :migration when :migration
"(#{migration_and_background_migration_folders.join('|')})" "(#{migration_and_background_migration_folders.join('|')})/"
when :all when :all
'' ''
else else
"(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})" "(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})/"
end end
end end
end end

View File

@ -987,10 +987,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235" resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g== integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
"@graphql-eslint/eslint-plugin@3.10.3": "@graphql-eslint/eslint-plugin@3.10.4":
version "3.10.3" version "3.10.4"
resolved "https://registry.yarnpkg.com/@graphql-eslint/eslint-plugin/-/eslint-plugin-3.10.3.tgz#5e77f18b18769fe68565a76cc33a094576809b5c" resolved "https://registry.yarnpkg.com/@graphql-eslint/eslint-plugin/-/eslint-plugin-3.10.4.tgz#fcc3b54ef1d6d38e89daf848d93be2d5359fef98"
integrity sha512-QQiTBw5T28INXayJHHp8lsfiJoanJIYuTqqA0axKlSyDMtNlbfsL3Kc+a+hth29Fw7zyi9XMGAhTDmIOkpskhg== integrity sha512-+Vmi1E5uSWOgtylosHS3HYWHF+x0GMgaMOHmSfOflTE51VXnLM2tvGKMd3lpyngCEZG1wjvqdlB5rn2iwhhzqA==
dependencies: dependencies:
"@babel/code-frame" "^7.16.7" "@babel/code-frame" "^7.16.7"
"@graphql-tools/code-file-loader" "^7.2.14" "@graphql-tools/code-file-loader" "^7.2.14"