Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3174adc799
commit
76784ebd4a
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module CheckCodeownerRules
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def codeowners_check_error(project, branch_name, paths)
|
||||
::Gitlab::CodeOwners::Validator.new(project, branch_name, paths).execute
|
||||
end
|
||||
end
|
||||
|
|
@ -9,6 +9,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
include ActionView::Helpers::SanitizeHelper
|
||||
include RedirectsForMissingPathOnTree
|
||||
include SourcegraphDecorator
|
||||
include CheckCodeownerRules
|
||||
|
||||
prepend_before_action :authenticate_user!, only: [:edit]
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
before_action :editor_variables, except: [:show, :preview, :diff]
|
||||
before_action :validate_diff_params, only: :diff
|
||||
before_action :set_last_commit_sha, only: [:edit, :update]
|
||||
before_action :validate_codeowner_rules, only: [:create, :update]
|
||||
|
||||
before_action only: :show do
|
||||
push_frontend_feature_flag(:code_navigation, @project)
|
||||
|
|
@ -71,6 +73,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
def update
|
||||
@path = params[:file_path] if params[:file_path].present?
|
||||
|
||||
create_commit(Files::UpdateService, success_path: -> { after_edit_path },
|
||||
failure_view: :edit,
|
||||
failure_path: project_blob_path(@project, @id))
|
||||
|
|
@ -115,6 +118,19 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def validate_codeowner_rules
|
||||
return if params[:file_path].blank?
|
||||
|
||||
codeowners_error = codeowners_check_error(@project, @branch_name, params[:file_path])
|
||||
|
||||
if codeowners_error.present?
|
||||
flash.now[:alert] = codeowners_error
|
||||
view = params[:action] == 'update' ? :edit : :new
|
||||
|
||||
render view
|
||||
end
|
||||
end
|
||||
|
||||
def blob
|
||||
@blob ||= @repository.blob_at(@commit.id, @path)
|
||||
|
||||
|
|
|
|||
|
|
@ -249,9 +249,7 @@ module Projects
|
|||
end
|
||||
end
|
||||
|
||||
# rubocop: disable Cop/InjectEnterpriseEditionModule
|
||||
Projects::CreateService.prepend_if_ee('EE::Projects::CreateService')
|
||||
# rubocop: enable Cop/InjectEnterpriseEditionModule
|
||||
|
||||
# Measurable should be at the bottom of the ancestor chain, so it will measure execution of EE::Projects::CreateService as well
|
||||
Projects::CreateService.prepend(Measurable)
|
||||
|
|
|
|||
|
|
@ -149,9 +149,7 @@ module Projects
|
|||
end
|
||||
end
|
||||
|
||||
# rubocop: disable Cop/InjectEnterpriseEditionModule
|
||||
Projects::ImportService.prepend_if_ee('EE::Projects::ImportService')
|
||||
# rubocop: enable Cop/InjectEnterpriseEditionModule
|
||||
|
||||
# Measurable should be at the bottom of the ancestor chain, so it will measure execution of EE::Projects::ImportService as well
|
||||
Projects::ImportService.prepend(Measurable)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ module Spam
|
|||
# ask the SpamVerdictService what to do with the target.
|
||||
spam_verdict_service.execute.tap do |result|
|
||||
case result
|
||||
when REQUIRE_RECAPTCHA
|
||||
when CONDITIONAL_ALLOW
|
||||
# at the moment, this means "ask for reCAPTCHA"
|
||||
create_spam_log(api)
|
||||
|
||||
break if target.allow_possible_spam?
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Spam
|
||||
module SpamConstants
|
||||
REQUIRE_RECAPTCHA = "recaptcha"
|
||||
CONDITIONAL_ALLOW = "conditional_allow"
|
||||
DISALLOW = "disallow"
|
||||
ALLOW = "allow"
|
||||
BLOCK_USER = "block"
|
||||
|
|
@ -14,7 +14,7 @@ module Spam
|
|||
DISALLOW => {
|
||||
priority: 2
|
||||
},
|
||||
REQUIRE_RECAPTCHA => {
|
||||
CONDITIONAL_ALLOW => {
|
||||
priority: 3
|
||||
},
|
||||
ALLOW => {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ module Spam
|
|||
|
||||
def akismet_verdict
|
||||
if akismet.spam?
|
||||
Gitlab::Recaptcha.enabled? ? REQUIRE_RECAPTCHA : DISALLOW
|
||||
Gitlab::Recaptcha.enabled? ? CONDITIONAL_ALLOW : DISALLOW
|
||||
else
|
||||
ALLOW
|
||||
end
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -111,6 +111,14 @@ module WorkerAttributes
|
|||
1
|
||||
end
|
||||
|
||||
def tags(*values)
|
||||
worker_attributes[:tags] = values
|
||||
end
|
||||
|
||||
def get_tags
|
||||
Array(worker_attributes[:tags])
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Returns a worker attribute declared on this class or its parent class.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add tags to experimental queue selector attributes
|
||||
merge_request: 32651
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Groups API has top_level_only option to exclude subgroups
|
||||
merge_request: 32870
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Skip the individual JIRA issues if failed to import vs failing the whole batch
|
||||
merge_request: 32673
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -126,6 +126,8 @@ in a more general way using the following components:
|
|||
|
||||
### Available attributes
|
||||
|
||||
- [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/261) in GitLab 13.1, `tags`.
|
||||
|
||||
From the [list of all available
|
||||
attributes](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/all_queues.yml),
|
||||
`experimental_queue_selector` allows selecting of queues by the
|
||||
|
|
@ -144,14 +146,21 @@ following attributes:
|
|||
- `name` - the queue name. The other attributes are typically more useful as
|
||||
they are more general, but this is available in case a particular queue needs
|
||||
to be selected.
|
||||
- `resource_boundary` - if the worker is bound by `cpu`, `memory`, or
|
||||
- `resource_boundary` - if the queue is bound by `cpu`, `memory`, or
|
||||
`unknown`. For example, the `project_export` queue is memory bound as it has
|
||||
to load data in memory before saving it for export.
|
||||
- `tags` - short-lived annotations for queues. These are expected to frequently
|
||||
change from release to release, and may be removed entirely.
|
||||
|
||||
`has_external_dependencies` is a boolean attribute: only the exact
|
||||
string `true` is considered true, and everything else is considered
|
||||
false.
|
||||
|
||||
`tags` is a set, which means that `=` checks for intersecting sets, and
|
||||
`!=` checks for disjoint sets. For example, `tags=a,b` selects queues
|
||||
that have tags `a`, `b`, or both. `tags!=a,b` selects queues that have
|
||||
neither of those tags.
|
||||
|
||||
### Available operators
|
||||
|
||||
`experimental_queue_selector` supports the following operators, listed
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Parameters:
|
|||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
|
||||
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
|
||||
| `top_level_only` | boolean | no | Limit to top level groups, excluding all subgroups |
|
||||
|
||||
```plaintext
|
||||
GET /groups
|
||||
|
|
|
|||
|
|
@ -3790,7 +3790,7 @@ Example:
|
|||
|
||||
.something_after: &something_after
|
||||
- echo 'something after'
|
||||
|
||||
- echo 'another thing after'
|
||||
|
||||
job_name:
|
||||
before_script:
|
||||
|
|
|
|||
|
|
@ -61,8 +61,12 @@ This helps maintain a logical connection and consistency between tools (e.g.
|
|||
|
||||
### Formality
|
||||
|
||||
The level of formality used in software varies by language.
|
||||
For example, in French we translate `you` as the formal `vous`.
|
||||
The level of formality used in software varies by language:
|
||||
|
||||
| Language | Formality | Example |
|
||||
| -------- | --------- | ------- |
|
||||
| French | formal | `vous` for `you` |
|
||||
| German | informal | `du` for `you` |
|
||||
|
||||
You can refer to other translated strings and notes in the glossary to assist
|
||||
determining a suitable level of formality.
|
||||
|
|
|
|||
|
|
@ -605,24 +605,22 @@ There are two formats of data in the JSON report that are used side by side:
|
|||
|
||||
### Other formats
|
||||
|
||||
Reports can also be generated in Markdown, HTML, and XML.
|
||||
|
||||
Reports can be published as artifacts using the following configuration:
|
||||
Reports can also be generated in Markdown, HTML, and XML. These can be published as artifacts using the following configuration:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
template: DAST.gitlab-ci.yml
|
||||
|
||||
dast:
|
||||
script:
|
||||
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
|
||||
- /analyze -r report.html -w report.md -x report.xml -t $DAST_WEBSITE
|
||||
- cp /zap/wrk/report.{html,md,xml} "$PWD"
|
||||
variables:
|
||||
DAST_HTML_REPORT: report.html
|
||||
DAST_MARKDOWN_REPORT: report.md
|
||||
DAST_XML_REPORT: report.xml
|
||||
artifacts:
|
||||
paths:
|
||||
- report.html
|
||||
- report.md
|
||||
- report.xml
|
||||
- $DAST_HTML_REPORT
|
||||
- $DAST_MARKDOWN_REPORT
|
||||
- $DAST_XML_REPORT
|
||||
- gl-dast-report.json
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,20 @@ module API
|
|||
optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
|
||||
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
|
||||
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
|
||||
optional :top_level_only, type: Boolean, desc: 'Only include top level groups'
|
||||
use :pagination
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def find_groups(params, parent_id = nil)
|
||||
find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
|
||||
find_params[:parent] = find_group!(parent_id) if parent_id
|
||||
|
||||
find_params[:parent] = if params[:top_level_only]
|
||||
[nil]
|
||||
elsif parent_id
|
||||
find_group!(parent_id)
|
||||
end
|
||||
|
||||
find_params[:all_available] =
|
||||
find_params.fetch(:all_available, current_user&.can_read_all_resources?)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,17 +57,27 @@ module Gitlab
|
|||
# For such cases we exit early if issue was already imported.
|
||||
next if already_imported?(jira_issue.id)
|
||||
|
||||
issue_attrs = IssueSerializer.new(project, jira_issue, running_import.user_id, { iid: next_iid }).execute
|
||||
Gitlab::JiraImport::ImportIssueWorker.perform_async(project.id, jira_issue.id, issue_attrs, job_waiter.key)
|
||||
begin
|
||||
issue_attrs = IssueSerializer.new(project, jira_issue, running_import.user_id, { iid: next_iid }).execute
|
||||
|
||||
job_waiter.jobs_remaining += 1
|
||||
next_iid += 1
|
||||
Gitlab::JiraImport::ImportIssueWorker.perform_async(project.id, jira_issue.id, issue_attrs, job_waiter.key)
|
||||
|
||||
# Mark the issue as imported immediately so we don't end up
|
||||
# importing it multiple times within same import.
|
||||
# These ids are cleaned-up when import finishes.
|
||||
# see Gitlab::JiraImport::Stage::FinishImportWorker
|
||||
mark_as_imported(jira_issue.id)
|
||||
job_waiter.jobs_remaining += 1
|
||||
next_iid += 1
|
||||
|
||||
# Mark the issue as imported immediately so we don't end up
|
||||
# importing it multiple times within same import.
|
||||
# These ids are cleaned-up when import finishes.
|
||||
# see Gitlab::JiraImport::Stage::FinishImportWorker
|
||||
mark_as_imported(jira_issue.id)
|
||||
rescue => ex
|
||||
# handle exceptionn here and skip the failed to import issue, instead of
|
||||
# failing to import the entire batch of issues
|
||||
|
||||
# track the failed to import issue.
|
||||
Gitlab::ErrorTracking.track_exception(ex, project_id: project.id)
|
||||
JiraImport.increment_issue_failures(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
job_waiter
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ module Gitlab
|
|||
].compact.freeze
|
||||
|
||||
DEFAULT_WORKERS = [
|
||||
DummyWorker.new('default', weight: 1),
|
||||
DummyWorker.new('mailers', weight: 2)
|
||||
DummyWorker.new('default', weight: 1, tags: []),
|
||||
DummyWorker.new('mailers', weight: 2, tags: [])
|
||||
].map { |worker| Gitlab::SidekiqConfig::Worker.new(worker, ee: false) }.freeze
|
||||
|
||||
class << self
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ module Gitlab
|
|||
has_external_dependencies: lambda { |value| value == 'true' },
|
||||
name: :to_s,
|
||||
resource_boundary: :to_sym,
|
||||
tags: :to_sym,
|
||||
urgency: :to_sym
|
||||
}.freeze
|
||||
|
||||
|
|
@ -117,7 +118,11 @@ module Gitlab
|
|||
|
||||
raise UnknownPredicate.new("Unknown predicate: #{lhs}") unless values_block
|
||||
|
||||
lambda { |queue| values.map(&values_block).include?(queue[lhs.to_sym]) }
|
||||
lambda do |queue|
|
||||
comparator = Array(queue[lhs.to_sym]).to_set
|
||||
|
||||
values.map(&values_block).to_set.intersect?(comparator)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ module Gitlab
|
|||
urgency: :get_urgency,
|
||||
resource_boundary: :get_worker_resource_boundary,
|
||||
idempotent: :idempotent?,
|
||||
weight: :get_weight
|
||||
weight: :get_weight,
|
||||
tags: :get_tags
|
||||
}.freeze
|
||||
|
||||
def initialize(queue, attributes = {})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
include Comparable
|
||||
|
||||
attr_reader :klass
|
||||
delegate :feature_category_not_owned?, :get_feature_category,
|
||||
delegate :feature_category_not_owned?, :get_feature_category, :get_tags,
|
||||
:get_urgency, :get_weight, :get_worker_resource_boundary,
|
||||
:idempotent?, :queue, :queue_namespace,
|
||||
:worker_has_external_dependencies?,
|
||||
|
|
@ -52,7 +52,8 @@ module Gitlab
|
|||
urgency: get_urgency,
|
||||
resource_boundary: get_worker_resource_boundary,
|
||||
weight: get_weight,
|
||||
idempotent: idempotent?
|
||||
idempotent: idempotent?,
|
||||
tags: get_tags
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
module RuboCop
|
||||
module Cop
|
||||
# Cop that blacklists the injecting of EE specific modules anywhere but on
|
||||
# the last line of a file. Injecting a module in the middle of a file will
|
||||
# cause merge conflicts, while placing it on the last line will not.
|
||||
# Cop that blacklists the injecting of EE specific modules before any lines which are not already injecting another module.
|
||||
# It allows multiple module injections as long as they're all at the end.
|
||||
class InjectEnterpriseEditionModule < RuboCop::Cop::Cop
|
||||
INVALID_LINE = 'Injecting EE modules must be done on the last line of this file' \
|
||||
', outside of any class or module definitions'
|
||||
|
|
@ -17,10 +16,12 @@ module RuboCop
|
|||
CHECK_LINE_METHODS =
|
||||
Set.new(%i[include_if_ee extend_if_ee prepend_if_ee]).freeze
|
||||
|
||||
CHECK_LINE_METHODS_REGEXP = Regexp.union(CHECK_LINE_METHODS.map(&:to_s)).freeze
|
||||
|
||||
DISALLOW_METHODS = Set.new(%i[include extend prepend]).freeze
|
||||
|
||||
COMMENT_OR_EMPTY_LINE = /^\s*(#.*|$)/.freeze
|
||||
|
||||
CHECK_LINE_METHODS_REGEXP = Regexp.union((CHECK_LINE_METHODS + DISALLOW_METHODS).map(&:to_s) + [COMMENT_OR_EMPTY_LINE]).freeze
|
||||
|
||||
def ee_const?(node)
|
||||
line = node.location.expression.source_line
|
||||
|
||||
|
|
@ -44,15 +45,11 @@ module RuboCop
|
|||
line = node.location.line
|
||||
buffer = node.location.expression.source_buffer
|
||||
last_line = buffer.last_line
|
||||
lines = buffer.source.split("\n")
|
||||
# We allow multiple includes, extends and prepends as long as they're all at the end.
|
||||
allowed_line = (line...last_line).all? { |i| CHECK_LINE_METHODS_REGEXP.match?(lines[i - 1]) }
|
||||
|
||||
# Parser treats the final newline (if present) as a separate line,
|
||||
# meaning that a simple `line < last_line` would yield true even though
|
||||
# the expression is the last line _of code_.
|
||||
last_line -= 1 if buffer.source.end_with?("\n")
|
||||
|
||||
last_line_content = buffer.source.split("\n")[-1]
|
||||
|
||||
if CHECK_LINE_METHODS_REGEXP.match?(last_line_content)
|
||||
if allowed_line
|
||||
ignore_node(node)
|
||||
elsif line < last_line
|
||||
add_offense(node, message: INVALID_LINE)
|
||||
|
|
|
|||
|
|
@ -250,6 +250,56 @@ describe Projects::BlobController do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples "file matches a codeowners rule" do
|
||||
let(:error_msg) { "Example error msg" }
|
||||
|
||||
it "renders to the edit page with an error msg" do
|
||||
default_params[:file_path] = "CHANGELOG"
|
||||
|
||||
expect_next_instance_of(Gitlab::CodeOwners::Validator) do |validator|
|
||||
expect(validator).to receive(:execute).and_return(error_msg)
|
||||
end
|
||||
|
||||
subject
|
||||
|
||||
expect(flash[:alert]).to eq(error_msg)
|
||||
expect(response).to render_template(expected_view)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
let(:user) { create(:user) }
|
||||
let(:default_params) do
|
||||
{
|
||||
namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
id: 'master',
|
||||
branch_name: 'master',
|
||||
file_name: 'CHANGELOG',
|
||||
content: 'Added changes',
|
||||
commit_message: 'Create CHANGELOG'
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'redirects to blob' do
|
||||
post :create, params: default_params
|
||||
|
||||
expect(response).to be_ok
|
||||
end
|
||||
|
||||
it_behaves_like "file matches a codeowners rule" do
|
||||
subject { post :create, params: default_params }
|
||||
|
||||
let(:expected_view) { :new }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT update' do
|
||||
let(:user) { create(:user) }
|
||||
let(:default_params) do
|
||||
|
|
@ -279,6 +329,12 @@ describe Projects::BlobController do
|
|||
expect(response).to redirect_to(blob_after_edit_path)
|
||||
end
|
||||
|
||||
it_behaves_like "file matches a codeowners rule" do
|
||||
subject { put :update, params: default_params }
|
||||
|
||||
let(:expected_view) { :edit }
|
||||
end
|
||||
|
||||
context '?from_merge_request_iid' do
|
||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:mr_params) { default_params.merge(from_merge_request_iid: merge_request.iid) }
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ describe Projects::IssuesController do
|
|||
before do
|
||||
stub_application_setting(recaptcha_enabled: true)
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -851,7 +851,7 @@ describe Projects::IssuesController do
|
|||
context 'when recaptcha is not verified' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1103,7 +1103,7 @@ describe Projects::IssuesController do
|
|||
context 'when captcha is not verified' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ describe 'New issue', :js do
|
|||
|
||||
before do
|
||||
allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
allow(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
allow(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
|
||||
visit new_project_issue_path(project)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ shared_examples_for 'snippet editor' do
|
|||
context 'when SpamVerdictService requires recaptcha' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,12 @@ describe GroupsFinder do
|
|||
let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
|
||||
let!(:private_subgroup) { create(:group, :private, parent: parent_group) }
|
||||
|
||||
context 'with [nil] parent' do
|
||||
it 'returns only top-level groups' do
|
||||
expect(described_class.new(user, parent: [nil]).execute).to contain_exactly(parent_group)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user' do
|
||||
it 'only returns parent and public subgroups' do
|
||||
expect(described_class.new(nil).execute).to contain_exactly(parent_group, public_subgroup)
|
||||
|
|
|
|||
|
|
@ -42,12 +42,19 @@ describe Gitlab::JiraImport::IssuesImporter do
|
|||
jira_issue = Struct.new(:id)
|
||||
let_it_be(:jira_issues) { [jira_issue.new(1), jira_issue.new(2)] }
|
||||
|
||||
def mock_issue_serializer(count)
|
||||
def mock_issue_serializer(count, raise_exception_on_even_mocks: false)
|
||||
serializer = instance_double(Gitlab::JiraImport::IssueSerializer, execute: { key: 'data' })
|
||||
next_iid = project.issues.maximum(:iid).to_i
|
||||
|
||||
count.times do |i|
|
||||
expect(Gitlab::JiraImport::IssueSerializer).to receive(:new)
|
||||
.with(project, jira_issues[i], current_user.id, { iid: i + 1 }).and_return(serializer)
|
||||
if raise_exception_on_even_mocks && i.even?
|
||||
expect(Gitlab::JiraImport::IssueSerializer).to receive(:new)
|
||||
.with(project, jira_issues[i], current_user.id, { iid: next_iid + 1 }).and_raise('Some error')
|
||||
else
|
||||
next_iid += 1
|
||||
expect(Gitlab::JiraImport::IssueSerializer).to receive(:new)
|
||||
.with(project, jira_issues[i], current_user.id, { iid: next_iid }).and_return(serializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -70,21 +77,22 @@ describe Gitlab::JiraImport::IssuesImporter do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when there is more than one page of results' do
|
||||
context 'when importing some issue raises an exception' do
|
||||
before do
|
||||
stub_const("#{described_class.name}::BATCH_SIZE", 2)
|
||||
stub_const("#{described_class.name}::BATCH_SIZE", 3)
|
||||
end
|
||||
|
||||
it 'schedules 3 import jobs' do
|
||||
it 'schedules 2 import jobs' do
|
||||
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issues[0], jira_issues[1]])
|
||||
expect(Gitlab::JiraImport::ImportIssueWorker).to receive(:perform_async).twice.times
|
||||
expect(Gitlab::Cache::Import::Caching).to receive(:set_add).twice.times.and_call_original
|
||||
expect(Gitlab::Cache::Import::Caching).to receive(:set_includes?).twice.times.and_call_original
|
||||
mock_issue_serializer(2)
|
||||
expect(Gitlab::JiraImport::ImportIssueWorker).to receive(:perform_async).once
|
||||
expect(Gitlab::Cache::Import::Caching).to receive(:set_add).once.and_call_original
|
||||
expect(Gitlab::Cache::Import::Caching).to receive(:set_includes?).twice.and_call_original
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).once
|
||||
mock_issue_serializer(2, raise_exception_on_even_mocks: true)
|
||||
|
||||
job_waiter = subject.execute
|
||||
|
||||
expect(job_waiter.jobs_remaining).to eq(2)
|
||||
expect(job_waiter.jobs_remaining).to eq(1)
|
||||
expect(Gitlab::JiraImport.get_issues_next_start_at(project.id)).to eq(2)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -117,28 +117,32 @@ describe Gitlab::SidekiqConfig::CliMethods do
|
|||
feature_category: :category_a,
|
||||
has_external_dependencies: false,
|
||||
urgency: :low,
|
||||
resource_boundary: :cpu
|
||||
resource_boundary: :cpu,
|
||||
tags: [:no_disk_io, :git_access]
|
||||
},
|
||||
{
|
||||
name: 'a:2',
|
||||
feature_category: :category_a,
|
||||
has_external_dependencies: false,
|
||||
urgency: :high,
|
||||
resource_boundary: :none
|
||||
resource_boundary: :none,
|
||||
tags: [:git_access]
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
feature_category: :category_b,
|
||||
has_external_dependencies: true,
|
||||
urgency: :high,
|
||||
resource_boundary: :memory
|
||||
resource_boundary: :memory,
|
||||
tags: [:no_disk_io]
|
||||
},
|
||||
{
|
||||
name: 'c',
|
||||
feature_category: :category_c,
|
||||
has_external_dependencies: false,
|
||||
urgency: :throttled,
|
||||
resource_boundary: :memory
|
||||
resource_boundary: :memory,
|
||||
tags: []
|
||||
}
|
||||
]
|
||||
end
|
||||
|
|
@ -177,6 +181,18 @@ describe Gitlab::SidekiqConfig::CliMethods do
|
|||
'resource_boundary=memory|resource_boundary=cpu' | %w(a b c)
|
||||
'resource_boundary!=memory,cpu' | %w(a:2)
|
||||
|
||||
# tags
|
||||
'tags=no_disk_io' | %w(a b)
|
||||
'tags=no_disk_io,git_access' | %w(a a:2 b)
|
||||
'tags=no_disk_io|tags=git_access' | %w(a a:2 b)
|
||||
'tags=no_disk_io&tags=git_access' | %w(a)
|
||||
'tags!=no_disk_io' | %w(a:2 c)
|
||||
'tags!=no_disk_io,git_access' | %w(c)
|
||||
'tags=unknown_tag' | []
|
||||
'tags!=no_disk_io' | %w(a:2 c)
|
||||
'tags!=no_disk_io,git_access' | %w(c)
|
||||
'tags!=unknown_tag' | %w(a a:2 b c)
|
||||
|
||||
# combinations
|
||||
'feature_category=category_a&urgency=high' | %w(a:2)
|
||||
'feature_category=category_a&urgency=high|feature_category=category_c' | %w(a:2 c)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
get_worker_resource_boundary: attributes[:resource_boundary],
|
||||
get_urgency: attributes[:urgency],
|
||||
worker_has_external_dependencies?: attributes[:has_external_dependencies],
|
||||
idempotent?: attributes[:idempotent]
|
||||
idempotent?: attributes[:idempotent],
|
||||
get_tags: attributes[:tags]
|
||||
)
|
||||
|
||||
described_class.new(inner_worker, ee: false)
|
||||
|
|
@ -91,7 +92,8 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
urgency: :low,
|
||||
resource_boundary: :memory,
|
||||
weight: 2,
|
||||
idempotent: true
|
||||
idempotent: true,
|
||||
tags: []
|
||||
}
|
||||
|
||||
attributes_b = {
|
||||
|
|
@ -100,7 +102,8 @@ describe Gitlab::SidekiqConfig::Worker do
|
|||
urgency: :high,
|
||||
resource_boundary: :unknown,
|
||||
weight: 3,
|
||||
idempotent: false
|
||||
idempotent: false,
|
||||
tags: [:no_disk_io]
|
||||
}
|
||||
|
||||
worker_a = create_worker(queue: 'a', **attributes_a)
|
||||
|
|
|
|||
|
|
@ -231,6 +231,27 @@ describe API::Groups do
|
|||
end
|
||||
end
|
||||
|
||||
context "when using top_level_only" do
|
||||
let(:top_level_group) { create(:group, name: 'top-level-group') }
|
||||
let(:subgroup) { create(:group, :nested, name: 'subgroup') }
|
||||
let(:response_groups) { json_response.map { |group| group['name'] } }
|
||||
|
||||
before do
|
||||
top_level_group.add_owner(user1)
|
||||
subgroup.add_owner(user1)
|
||||
end
|
||||
|
||||
it "doesn't return subgroups" do
|
||||
get api("/groups", user1), params: { top_level_only: true }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_an Array
|
||||
expect(response_groups).to include(top_level_group.name)
|
||||
expect(response_groups).not_to include(subgroup.name)
|
||||
end
|
||||
end
|
||||
|
||||
context "when using sorting" do
|
||||
let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
|
||||
let(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") }
|
||||
|
|
|
|||
|
|
@ -170,6 +170,20 @@ describe RuboCop::Cop::InjectEnterpriseEditionModule do
|
|||
SOURCE
|
||||
end
|
||||
|
||||
it 'does not flag the use of `prepend_if_ee EE` as long as all injections are at the end of the file' do
|
||||
expect_no_offenses(<<~SOURCE)
|
||||
class Foo
|
||||
end
|
||||
|
||||
Foo.include_if_ee('EE::Foo')
|
||||
Foo.prepend_if_ee('EE::Foo')
|
||||
|
||||
Foo.include(Bar)
|
||||
# comment on prepending Bar
|
||||
Foo.prepend(Bar)
|
||||
SOURCE
|
||||
end
|
||||
|
||||
it 'autocorrects offenses by just disabling the Cop' do
|
||||
source = <<~SOURCE
|
||||
class Foo
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ describe Issues::CreateService do
|
|||
context 'when SpamVerdictService requires reCAPTCHA' do
|
||||
before do
|
||||
expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
|
||||
expect(verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -163,9 +163,9 @@ describe Spam::SpamActionService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when spam verdict service requires reCAPTCHA' do
|
||||
context 'when spam verdict service conditionally allows' do
|
||||
before do
|
||||
allow(fake_verdict_service).to receive(:execute).and_return(REQUIRE_RECAPTCHA)
|
||||
allow(fake_verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
|
||||
end
|
||||
|
||||
context 'when allow_possible_spam feature flag is false' do
|
||||
|
|
|
|||
|
|
@ -123,8 +123,8 @@ describe Spam::SpamVerdictService do
|
|||
stub_application_setting(recaptcha_enabled: true)
|
||||
end
|
||||
|
||||
it 'returns require reCAPTCHA verdict' do
|
||||
expect(subject).to eq REQUIRE_RECAPTCHA
|
||||
it 'returns conditionally allow verdict' do
|
||||
expect(subject).to eq CONDITIONAL_ALLOW
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
shared_context 'includes Spam constants' do
|
||||
before do
|
||||
stub_const('REQUIRE_RECAPTCHA', Spam::SpamConstants::REQUIRE_RECAPTCHA)
|
||||
stub_const('CONDITIONAL_ALLOW', Spam::SpamConstants::CONDITIONAL_ALLOW)
|
||||
stub_const('DISALLOW', Spam::SpamConstants::DISALLOW)
|
||||
stub_const('ALLOW', Spam::SpamConstants::ALLOW)
|
||||
stub_const('BLOCK_USER', Spam::SpamConstants::BLOCK_USER)
|
||||
|
|
|
|||
Loading…
Reference in New Issue