Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-31 03:10:36 +00:00
parent bef48349bb
commit 853c0c530b
15 changed files with 285 additions and 415 deletions

View File

@ -187,7 +187,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.17'
gem 'asciidoctor-include-ext', '~> 0.4.0', require: false
gem 'asciidoctor-plantuml', '~> 0.0.16'
gem 'asciidoctor-kroki', '~> 0.5.0', require: false
gem 'asciidoctor-kroki', '~> 0.7.0', require: false
gem 'rouge', '~> 3.30.0'
gem 'truncato', '~> 0.7.12'
gem 'bootstrap_form', '~> 4.2.0'

View File

@ -24,7 +24,7 @@
{"name":"asana","version":"0.10.13","platform":"ruby","checksum":"36d0d37f8dd6118a54580f1b80224875d7b6a9027598938e1722a508bfc2d7ac"},
{"name":"asciidoctor","version":"2.0.17","platform":"ruby","checksum":"ed5b5e399e8d64994cc16f0983f993d6e33990909a8415b6fc8b786cdeb00f3d"},
{"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"},
{"name":"asciidoctor-kroki","version":"0.5.0","platform":"ruby","checksum":"622c8b74796689bdaf8abdf89ad5295b11ce310e3d193e28f19e5baf58d45f12"},
{"name":"asciidoctor-kroki","version":"0.7.0","platform":"ruby","checksum":"528ae4e49cae10e98c76e91f9aa40c67bf8540aa5ce4bbd44c5cd57af9f0b121"},
{"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"},
{"name":"ast","version":"2.4.2","platform":"ruby","checksum":"1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12"},
{"name":"atlassian-jwt","version":"0.2.0","platform":"ruby","checksum":"52e653e9d6062d7a740c3675b0e79fa08367927c6fc17f5476d1b6b3798c6eb2"},

View File

@ -172,7 +172,7 @@ GEM
asciidoctor (2.0.17)
asciidoctor-include-ext (0.4.0)
asciidoctor (>= 1.5.6, < 3.0.0)
asciidoctor-kroki (0.5.0)
asciidoctor-kroki (0.7.0)
asciidoctor (~> 2.0)
asciidoctor-plantuml (0.0.16)
asciidoctor (>= 2.0.17, < 3.0.0)
@ -1549,7 +1549,7 @@ DEPENDENCIES
asana (~> 0.10.13)
asciidoctor (~> 2.0.17)
asciidoctor-include-ext (~> 0.4.0)
asciidoctor-kroki (~> 0.5.0)
asciidoctor-kroki (~> 0.7.0)
asciidoctor-plantuml (~> 0.0.16)
atlassian-jwt (~> 0.2.0)
attr_encrypted (~> 3.2.4)!

View File

@ -12,7 +12,10 @@ module Members
old_access_level = member.human_access
old_expiry = member.expires_at
if member.update(params)
member.attributes = params
return success(member: member) unless member.changed?
if member.save
after_execute(action: permission, old_access_level: old_access_level, old_expiry: old_expiry, member: member)
# Deletes only confidential issues todos for guests

View File

@ -16,9 +16,9 @@ You can use the Freeze Periods API to manipulate GitLab [Freeze Period](../user/
Only users with Maintainer [permissions](../user/permissions.md) can
interact with the Freeze Period API endpoints.
## List Freeze Periods
## List freeze periods
Paginated list of Freeze Periods, sorted by `created_at` in ascending order.
Paginated list of freeze periods, sorted by `created_at` in ascending order.
```plaintext
GET /projects/:id/freeze_periods
@ -49,9 +49,9 @@ Example response:
]
```
## Get a Freeze Period by a `freeze_period_id`
## Get a freeze period by a `freeze_period_id`
Get a Freeze Period for the given `freeze_period_id`.
Get a freeze period for the given `freeze_period_id`.
```plaintext
GET /projects/:id/freeze_periods/:freeze_period_id
@ -60,7 +60,7 @@ GET /projects/:id/freeze_periods/:freeze_period_id
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `freeze_period_id` | string | yes | The database ID of the Freeze Period. |
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
Example request:
@ -81,9 +81,9 @@ Example response:
}
```
## Create a Freeze Period
## Create a freeze period
Create a Freeze Period.
Create a freeze period.
```plaintext
POST /projects/:id/freeze_periods
@ -92,8 +92,8 @@ POST /projects/:id/freeze_periods
| Attribute | Type | Required | Description |
| -------------------| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `freeze_start` | string | yes | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | yes | End of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_start` | string | yes | Start of the freeze period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | yes | End of the freeze period in [cron](https://crontab.guru/) format. |
| `cron_timezone` | string | no | The time zone for the cron fields, defaults to UTC if not provided. |
Example request:
@ -117,9 +117,9 @@ Example response:
}
```
## Update a Freeze Period
## Update a freeze period
Update a Freeze Period for the given `freeze_period_id`.
Update a freeze period for the given `freeze_period_id`.
```plaintext
PUT /projects/:id/freeze_periods/:freeze_period_id
@ -128,9 +128,9 @@ PUT /projects/:id/freeze_periods/:freeze_period_id
| Attribute | Type | Required | Description |
| ------------- | --------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `freeze_period_id` | integer or string | yes | The database ID of the Freeze Period. |
| `freeze_start` | string | no | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | no | End of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
| `freeze_start` | string | no | Start of the freeze period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | no | End of the freeze period in [cron](https://crontab.guru/) format. |
| `cron_timezone` | string | no | The time zone for the cron fields. |
Example request:
@ -154,9 +154,9 @@ Example response:
}
```
## Delete a Freeze Period
## Delete a freeze period
Delete a Freeze Period for the given `freeze_period_id`.
Delete a freeze period for the given `freeze_period_id`.
```plaintext
DELETE /projects/:id/freeze_periods/:freeze_period_id
@ -165,7 +165,7 @@ DELETE /projects/:id/freeze_periods/:freeze_period_id
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `freeze_period_id` | string | yes | The database ID of the Freeze Period. |
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
Example request:

View File

@ -13,9 +13,9 @@ links. For manipulating other Release assets, see [Release API](index.md).
GitLab supports links to `http`, `https`, and `ftp` assets.
## Get links
## List links of a release
Get assets as links from a Release.
Get assets as links from a release.
```plaintext
GET /projects/:id/releases/:tag_name/assets/links
@ -53,9 +53,9 @@ Example response:
]
```
## Get a link
## Get a release link
Get an asset as a link from a Release.
Get an asset as a link from a release.
```plaintext
GET /projects/:id/releases/:tag_name/assets/links/:link_id
@ -85,9 +85,9 @@ Example response:
}
```
## Create a link
## Create a release link
Create an asset as a link from a Release.
Creates an asset as a link from a release.
```plaintext
POST /projects/:id/releases/:tag_name/assets/links
@ -126,9 +126,9 @@ Example response:
}
```
## Update a link
## Update a release link
Update an asset as a link from a Release.
Updates an asset as a link from a release.
```plaintext
PUT /projects/:id/releases/:tag_name/assets/links/:link_id
@ -167,9 +167,9 @@ Example response:
}
```
## Delete a link
## Delete a release link
Delete an asset as a link from a Release.
Deletes an asset as a link from a release.
```plaintext
DELETE /projects/:id/releases/:tag_name/assets/links/:link_id

View File

@ -93,7 +93,7 @@ By default, GitLab fetches the release using `released_at` time. The use of the
The assets associated with a release are accessible through a permanent URL.
GitLab always redirects this URL to the actual asset
location, so even if the assets move to a different location, you can continue
to use the same URL. This is defined during [link creation](../../../api/releases/links.md#create-a-link) or [updating](../../../api/releases/links.md#update-a-link) using the `filepath` API attribute.
to use the same URL. This is defined during [link creation](../../../api/releases/links.md#create-a-release-link) or [updating](../../../api/releases/links.md#update-a-release-link) using the `filepath` API attribute.
The format of the URL is:
@ -133,7 +133,7 @@ The format of the URL is:
https://host/namespace/project/-/releases/permalink/latest/downloads/:filepath
```
If you have an asset with [`filepath`](../../../api/releases/links.md#create-a-link) for the `v11.9.0-rc2` latest release in the `gitlab-org`
If you have an asset with [`filepath`](../../../api/releases/links.md#create-a-release-link) for the `v11.9.0-rc2` latest release in the `gitlab-org`
namespace and `gitlab-runner` project on `gitlab.com`, for example:
```json

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true
require "nokogiri"
require "asciidoctor/extensions/asciidoctor_kroki/extension"
require 'nokogiri'
require 'asciidoctor/extensions/asciidoctor_kroki/version'
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
module Banzai
module Filter

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'rouge/plugins/common_mark'
require "asciidoctor/extensions/asciidoctor_kroki/extension"
require 'asciidoctor/extensions/asciidoctor_kroki/version'
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js
module Banzai

View File

@ -2,6 +2,7 @@
require 'asciidoctor'
require 'asciidoctor-plantuml'
require 'asciidoctor/extensions/asciidoctor_kroki/version'
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
require 'asciidoctor/extensions'
require 'gitlab/asciidoc/html5_converter'

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'asciidoctor/extensions/asciidoctor_kroki/version'
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
module Gitlab

View File

@ -567,7 +567,7 @@ module Gitlab
it 'does not allow kroki-plantuml-include to be overridden' do
input = <<~ADOC
[plantuml, test="{counter:kroki-plantuml-include:/etc/passwd}", format="png"]
[plantuml, test="{counter:kroki-plantuml-include:README.md}", format="png"]
....
class BlockProcessor
@ -578,7 +578,7 @@ module Gitlab
output = <<~HTML
<div>
<div>
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\"></a>
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\"></a>
</div>
</div>
HTML

View File

@ -6,7 +6,8 @@ RSpec.describe Gitlab::Kroki do
describe '.formats' do
def default_formats
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml svgbob umlet vega vegalite wavedrom].freeze
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml
structurizr svgbob umlet vega vegalite wavedrom].freeze
end
subject { described_class.formats(Gitlab::CurrentSettings) }

View File

@ -217,120 +217,80 @@ RSpec.describe Gitlab::Redis::MultiStore do
allow(secondary_store).to receive(name).and_call_original
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
context 'when reading from the primary is successful' do
it 'returns the correct value' do
expect(primary_store).to receive(name).with(*args).and_call_original
subject
end
it 'does not execute on the secondary store' do
expect(secondary_store).not_to receive(name)
subject
end
include_examples 'reads correct value'
end
context 'when reading from primary instance is raising an exception' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
allow(primary_store).to receive(name).with(*args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
context 'when reading from the primary is successful' do
it 'returns the correct value' do
expect(primary_store).to receive(name).with(*args).and_call_original
it 'logs the exception' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, instance_name: instance_name, command_name: name))
subject
end
it 'does not execute on the secondary store' do
expect(secondary_store).not_to receive(name)
subject
end
include_examples 'reads correct value'
subject
end
context 'when reading from primary instance is raising an exception' do
before do
allow(primary_store).to receive(name).with(*args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
include_examples 'fallback read from the secondary store'
end
it 'logs the exception' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, instance_name: instance_name, command_name: name))
subject
end
include_examples 'fallback read from the secondary store'
context 'when reading from primary instance return no value' do
before do
allow(primary_store).to receive(name).and_return(nil)
end
context 'when reading from primary instance return no value' do
before do
allow(primary_store).to receive(name).and_return(nil)
end
include_examples 'fallback read from the secondary store'
end
include_examples 'fallback read from the secondary store'
context 'when the command is executed within pipelined block' do
subject do
multi_store.pipelined do |pipeline|
pipeline.send(name, *args)
end
end
context 'when the command is executed within pipelined block' do
subject do
multi_store.pipelined do |pipeline|
pipeline.send(name, *args)
it 'is executed only 1 time on primary and secondary instance' do
expect(primary_store).to receive(:pipelined).and_call_original
expect(secondary_store).to receive(:pipelined).and_call_original
2.times do
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*args).once.and_call_original
end
end
it 'is executed only 1 time on primary and secondary instance' do
expect(primary_store).to receive(:pipelined).and_call_original
expect(secondary_store).to receive(:pipelined).and_call_original
2.times do
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*args).once.and_call_original
end
end
subject
end
end
if params[:block]
subject do
multi_store.send(name, *args, &block)
end
context 'when block is provided' do
it 'yields to the block' do
expect(primary_store).to receive(name).and_yield(value)
subject
end
include_examples 'reads correct value'
end
subject
end
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
if params[:block]
subject do
multi_store.send(name, *args, &block)
end
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
it_behaves_like 'secondary store'
end
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
end
it 'execute on the primary instance' do
expect(primary_store).to receive(name).with(*args).and_call_original
context 'when block is provided' do
it 'yields to the block' do
expect(primary_store).to receive(name).and_yield(value)
subject
end
include_examples 'reads correct value'
it 'does not execute on the secondary store' do
expect(secondary_store).not_to receive(name)
subject
end
end
end
@ -411,100 +371,58 @@ RSpec.describe Gitlab::Redis::MultiStore do
allow(secondary_store).to receive(name).and_call_original
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
context 'when executing on primary instance is successful' do
it 'executes on both primary and secondary redis store', :aggregate_errors do
expect(primary_store).to receive(name).with(*expected_args).and_call_original
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
subject
end
context 'when executing on primary instance is successful' do
it 'executes on both primary and secondary redis store', :aggregate_errors do
expect(primary_store).to receive(name).with(*expected_args).and_call_original
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
subject
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
context 'when executing on the primary instance is raising an exception' do
before do
allow(primary_store).to receive(name).with(*expected_args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
it 'logs the exception and execute on secondary instance', :aggregate_errors do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
subject
end
include_examples 'verify that store contains values', :secondary_store
end
context 'when the command is executed within pipelined block' do
subject do
multi_store.pipelined do |pipeline|
pipeline.send(name, *args)
end
end
it 'is executed only 1 time on each instance', :aggregate_errors do
expect(primary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
end
expect(secondary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
end
subject
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
context 'when executing on the primary instance is raising an exception' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
allow(primary_store).to receive(name).with(*expected_args).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
it 'logs the exception and execute on secondary instance', :aggregate_errors do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
it 'executes only on the secondary redis store', :aggregate_errors do
expect(secondary_store).to receive(name).with(*expected_args)
expect(primary_store).not_to receive(name).with(*expected_args)
subject
end
include_examples 'verify that store contains values', :secondary_store
subject
end
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
include_examples 'verify that store contains values', :secondary_store
end
context 'when the command is executed within pipelined block' do
subject do
multi_store.pipelined do |pipeline|
pipeline.send(name, *args)
end
it 'executes only on the primary_redis redis store', :aggregate_errors do
expect(primary_store).to receive(name).with(*expected_args)
expect(secondary_store).not_to receive(name).with(*expected_args)
subject
end
include_examples 'verify that store contains values', :primary_store
end
it 'is executed only 1 time on each instance', :aggregate_errors do
expect(primary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
end
expect(secondary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
end
subject
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
end
end
@ -539,151 +457,109 @@ RSpec.describe Gitlab::Redis::MultiStore do
end
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
context 'when executing on primary instance is successful' do
it 'executes on both primary and secondary redis store', :aggregate_errors do
expect(primary_store).to receive(name).and_call_original
expect(secondary_store).to receive(name).and_call_original
subject
end
context 'when executing on primary instance is successful' do
it 'executes on both primary and secondary redis store', :aggregate_errors do
expect(primary_store).to receive(name).and_call_original
expect(secondary_store).to receive(name).and_call_original
subject
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
context 'when executing on the primary instance is raising an exception' do
before do
allow(primary_store).to receive(name).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
it 'logs the exception and execute on secondary instance', :aggregate_errors do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name))
expect(secondary_store).to receive(name).and_call_original
subject
end
include_examples 'verify that store contains values', :secondary_store
end
describe 'return values from a pipelined command' do
RSpec::Matchers.define :pipeline_diff_error_with_stacktrace do |message|
match do |object|
expect(object).to be_a(Gitlab::Redis::MultiStore::PipelinedDiffError)
expect(object.backtrace).not_to be_nil
expect(object.message).to eq(message)
end
end
subject do
multi_store.send(name) do |redis|
redis.get(key1)
end
end
context 'when the value exists on both and are equal' do
before do
primary_store.set(key1, value1)
secondary_store.set(key1, value1)
end
it 'returns the value' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect(subject).to eq([value1])
end
end
context 'when the value exists on both but differ' do
before do
primary_store.set(key1, value1)
secondary_store.set(key1, value2)
end
it 'returns the value from the secondary store, logging an error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
pipeline_diff_error_with_stacktrace(
'Pipelined command executed on both stores successfully but results differ between them. ' \
"Result from the primary: [#{value1.inspect}]. Result from the secondary: [#{value2.inspect}]."
),
hash_including(command_name: name, instance_name: instance_name)
).and_call_original
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
expect(subject).to eq([value2])
end
end
context 'when the value does not exist on the primary but it does on the secondary' do
before do
secondary_store.set(key1, value2)
end
it 'returns the value from the secondary store, logging an error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
pipeline_diff_error_with_stacktrace(
'Pipelined command executed on both stores successfully but results differ between them. ' \
"Result from the primary: [nil]. Result from the secondary: [#{value2.inspect}]."
),
hash_including(command_name: name, instance_name: instance_name)
)
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
expect(subject).to eq([value2])
end
end
context 'when the value does not exist in either' do
it 'returns nil without logging an error' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect(counter).not_to receive(:increment)
expect(subject).to eq([nil])
end
end
end
include_examples 'verify that store contains values', :primary_store
include_examples 'verify that store contains values', :secondary_store
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
context 'when executing on the primary instance is raising an exception' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
allow(primary_store).to receive(name).and_raise(StandardError)
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
it 'logs the exception and execute on secondary instance', :aggregate_errors do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name))
expect(secondary_store).to receive(name).and_call_original
it 'executes only on the secondary redis store', :aggregate_errors do
expect(secondary_store).to receive(name)
expect(primary_store).not_to receive(name)
subject
end
include_examples 'verify that store contains values', :secondary_store
subject
end
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
include_examples 'verify that store contains values', :secondary_store
end
describe 'return values from a pipelined command' do
RSpec::Matchers.define :pipeline_diff_error_with_stacktrace do |message|
match do |object|
expect(object).to be_a(Gitlab::Redis::MultiStore::PipelinedDiffError)
expect(object.backtrace).not_to be_nil
expect(object.message).to eq(message)
end
end
subject do
multi_store.send(name) do |redis|
redis.get(key1)
end
end
context 'when the value exists on both and are equal' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
primary_store.set(key1, value1)
secondary_store.set(key1, value1)
end
it 'executes only on the primary_redis redis store', :aggregate_errors do
expect(primary_store).to receive(name)
expect(secondary_store).not_to receive(name)
it 'returns the value' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
subject
expect(subject).to eq([value1])
end
end
context 'when the value exists on both but differ' do
before do
primary_store.set(key1, value1)
secondary_store.set(key1, value2)
end
include_examples 'verify that store contains values', :primary_store
it 'returns the value from the secondary store, logging an error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
pipeline_diff_error_with_stacktrace(
'Pipelined command executed on both stores successfully but results differ between them. ' \
"Result from the primary: [#{value1.inspect}]. Result from the secondary: [#{value2.inspect}]."
),
hash_including(command_name: name, instance_name: instance_name)
).and_call_original
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
expect(subject).to eq([value2])
end
end
context 'when the value does not exist on the primary but it does on the secondary' do
before do
secondary_store.set(key1, value2)
end
it 'returns the value from the secondary store, logging an error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
pipeline_diff_error_with_stacktrace(
'Pipelined command executed on both stores successfully but results differ between them. ' \
"Result from the primary: [nil]. Result from the secondary: [#{value2.inspect}]."
),
hash_including(command_name: name, instance_name: instance_name)
)
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
expect(subject).to eq([value2])
end
end
context 'when the value does not exist in either' do
it 'returns nil without logging an error' do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect(counter).not_to receive(:increment)
expect(subject).to eq([nil])
end
end
end
end
@ -827,40 +703,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
describe '#to_s' do
subject { multi_store.to_s }
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
end
it 'returns same value as primary_store' do
is_expected.to eq(primary_store.to_s)
end
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
end
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
end
it 'returns same value as primary_store' do
is_expected.to eq(primary_store.to_s)
end
end
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
it 'returns same value as primary_store' do
is_expected.to eq(secondary_store.to_s)
end
end
it 'returns same value as primary_store' do
is_expected.to eq(primary_store.to_s)
end
end
@ -871,24 +715,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
end
describe '#use_primary_and_secondary_stores?' do
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
end
it 'multi store is disabled' do
expect(multi_store.use_primary_and_secondary_stores?).to be true
end
end
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
end
it 'multi store is disabled' do
expect(multi_store.use_primary_and_secondary_stores?).to be false
end
it 'multi store is enabled' do
expect(multi_store.use_primary_and_secondary_stores?).to be true
end
context 'with empty DB' do
@ -913,24 +741,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
end
describe '#use_primary_store_as_default?' do
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
end
it 'multi store is disabled' do
expect(multi_store.use_primary_store_as_default?).to be true
end
end
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
before do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
it 'multi store is disabled' do
expect(multi_store.use_primary_store_as_default?).to be false
end
it 'multi store is disabled' do
expect(multi_store.use_primary_store_as_default?).to be true
end
context 'with empty DB' do

View File

@ -14,6 +14,11 @@ RSpec.describe Members::UpdateService do
{ access_level: access_level }
end
before do
project.add_developer(member_user)
group.add_developer(member_user)
end
subject { described_class.new(current_user, params).execute(member, permission: permission) }
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
@ -83,11 +88,6 @@ RSpec.describe Members::UpdateService do
end
end
before do
project.add_developer(member_user)
group.add_developer(member_user)
end
context 'when current user cannot update the given member' do
it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
let(:source) { project }
@ -183,4 +183,54 @@ RSpec.describe Members::UpdateService do
end
end
end
context 'authorization updates' do
let_it_be(:user) { create(:user) }
shared_examples 'manages authorization updates' do
context 'access level changes' do
let(:params) do
{ access_level: Gitlab::Access::MAINTAINER }
end
it 'authorization update callback is triggered' do
expect(member).to receive(:refresh_member_authorized_projects).once
described_class.new(current_user, params).execute(member, permission: permission)
end
end
context 'no attribute changes' do
let(:params) do
{ access_level: Gitlab::Access::DEVELOPER }
end
it 'authorization update callback is not triggered' do
expect(member).not_to receive(:refresh_member_authorized_projects)
described_class.new(current_user, params).execute(member, permission: permission)
end
end
end
context 'group member' do
let(:source) { group }
before do
group.add_owner(current_user)
end
include_examples 'manages authorization updates'
end
context 'project member' do
let(:source) { project }
before do
project.add_maintainer(current_user)
end
include_examples 'manages authorization updates'
end
end
end