Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
60a260df41
commit
013a89081d
21 changed files with 370 additions and 330 deletions
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { MEMBER_TYPES } from '../../constants';
|
||||
import { MEMBER_TYPES, EE_ACTION_BUTTONS } from 'ee_else_ce/members/constants';
|
||||
import AccessRequestActionButtons from '../action_buttons/access_request_action_buttons.vue';
|
||||
import GroupActionButtons from '../action_buttons/group_action_buttons.vue';
|
||||
import InviteActionButtons from '../action_buttons/invite_action_buttons.vue';
|
||||
|
@ -12,6 +12,8 @@ export default {
|
|||
GroupActionButtons,
|
||||
InviteActionButtons,
|
||||
AccessRequestActionButtons,
|
||||
BannedActionButtons: () =>
|
||||
import('ee_component/members/components/action_buttons/banned_action_buttons.vue'),
|
||||
},
|
||||
props: {
|
||||
member: {
|
||||
|
@ -42,6 +44,7 @@ export default {
|
|||
[MEMBER_TYPES.group]: 'group-action-buttons',
|
||||
[MEMBER_TYPES.invite]: 'invite-action-buttons',
|
||||
[MEMBER_TYPES.accessRequest]: 'access-request-action-buttons',
|
||||
...EE_ACTION_BUTTONS,
|
||||
};
|
||||
|
||||
return dictionary[this.memberType];
|
||||
|
|
|
@ -9,6 +9,8 @@ export const EE_APP_OPTIONS = {};
|
|||
// Overridden in EE
|
||||
export const EE_TABS = [];
|
||||
|
||||
export const EE_ACTION_BUTTONS = {};
|
||||
|
||||
export const FIELD_KEY_ACCOUNT = 'account';
|
||||
export const FIELD_KEY_SOURCE = 'source';
|
||||
export const FIELD_KEY_GRANTED = 'granted';
|
||||
|
|
|
@ -171,7 +171,7 @@ module IssuableActions
|
|||
discussions = Discussion.build_collection(notes, issuable)
|
||||
|
||||
if issuable.is_a?(MergeRequest)
|
||||
render_cached(discussions, with: discussion_serializer, cache_context: -> (_) { discussion_cache_context }, context: self)
|
||||
render_mr_discussions(discussions, discussion_serializer, discussion_cache_context)
|
||||
elsif issuable.is_a?(Issue)
|
||||
render json: discussion_serializer.represent(discussions, context: self) if stale?(etag: [discussion_cache_context, discussions])
|
||||
else
|
||||
|
@ -182,6 +182,24 @@ module IssuableActions
|
|||
|
||||
private
|
||||
|
||||
def render_mr_discussions(discussions, serializer, cache_context)
|
||||
if Feature.enabled?(:mr_discussions_http_cache, project)
|
||||
return unless stale?(etag: [cache_context, discussions])
|
||||
|
||||
if Feature.enabled?(:disabled_mr_discussions_redis_cache, project)
|
||||
render json: serializer.represent(discussions, context: self)
|
||||
else
|
||||
render_cached_discussions(discussions, serializer, cache_context)
|
||||
end
|
||||
else
|
||||
render_cached_discussions(discussions, serializer, cache_context)
|
||||
end
|
||||
end
|
||||
|
||||
def render_cached_discussions(discussions, serializer, cache_context)
|
||||
render_cached(discussions, with: serializer, cache_context: -> (_) { cache_context }, context: self)
|
||||
end
|
||||
|
||||
def paginated_discussions
|
||||
return if params[:per_page].blank?
|
||||
return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, project)
|
||||
|
|
|
@ -14,7 +14,7 @@ module Resolvers
|
|||
response = ::ErrorTracking::IssueDetailsService.new(
|
||||
project,
|
||||
current_user,
|
||||
{ issue_id: id.model_id }
|
||||
{ issue_id: id.model_id, tracking_event: :error_tracking_view_details }
|
||||
).execute
|
||||
issue = response[:issue]
|
||||
issue.gitlab_project = project if issue
|
||||
|
|
|
@ -182,7 +182,7 @@ module GroupsHelper
|
|||
|
||||
def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false)
|
||||
link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
|
||||
icon = group_icon(group, class: "avatar-tile", width: 15, height: 15) if (group.try(:avatar_url) || show_avatar) && !Rails.env.test?
|
||||
icon = group_icon(group, alt: group.name, class: "avatar-tile", width: 15, height: 15) if group.try(:avatar_url) || show_avatar
|
||||
[icon, simple_sanitize(group.name)].join.html_safe
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: disabled_mr_discussions_redis_cache
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92752
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368366
|
||||
milestone: '15.3'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: mr_discussions_http_cache
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92752
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368363
|
||||
milestone: '15.3'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -56,6 +56,7 @@ exceptions:
|
|||
- DML
|
||||
- DNS
|
||||
- DOM
|
||||
- DORA
|
||||
- DSA
|
||||
- DSL
|
||||
- DVCS
|
||||
|
|
|
@ -17,20 +17,6 @@ Troubleshooting Elasticsearch requires:
|
|||
- Knowledge of common terms.
|
||||
- Establishing within which category the problem fits.
|
||||
|
||||
## Common terminology
|
||||
|
||||
- **Lucene**: A full-text search library written in Java.
|
||||
- **Near real time (NRT)**: Refers to the slight latency from the time to index a
|
||||
document to the time when it becomes searchable.
|
||||
- **Cluster**: A collection of one or more nodes that work together to hold all
|
||||
the data, providing indexing and search capabilities.
|
||||
- **Node**: A single server that works as part of a cluster.
|
||||
- **Index**: A collection of documents that have somewhat similar characteristics.
|
||||
- **Document**: A basic unit of information that can be indexed.
|
||||
- **Shards**: Fully-functional and independent subdivisions of indices. Each shard is actually
|
||||
a Lucene index.
|
||||
- **Replicas**: Failover mechanisms that duplicate indices.
|
||||
|
||||
## Troubleshooting workflows
|
||||
|
||||
The type of problem will determine what steps to take. The possible troubleshooting workflows are for:
|
||||
|
|
|
@ -150,10 +150,10 @@ Dockerfiles to build and deploy <https://docs.gitlab.com>. It is heavily inspire
|
|||
|
||||
| Dockerfile | Docker image | Description |
|
||||
|:---------------------------------------------------------------------------------------------------------------------------|:------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`Dockerfile.bootstrap`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/Dockerfile.bootstrap) | `gitlab-docs:bootstrap` | Contains all the dependencies that are needed to build the website. If the gems are updated and `Gemfile{,.lock}` changes, the image must be rebuilt. |
|
||||
| [`Dockerfile.builder.onbuild`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/Dockerfile.builder.onbuild) | `gitlab-docs:builder-onbuild` | Base image to build the docs website. It uses `ONBUILD` to perform all steps and depends on `gitlab-docs:bootstrap`. |
|
||||
| [`Dockerfile.nginx.onbuild`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/Dockerfile.nginx.onbuild) | `gitlab-docs:nginx-onbuild` | Base image to use for building documentation archives. It uses `ONBUILD` to perform all required steps to copy the archive, and relies upon its parent `Dockerfile.builder.onbuild` that is invoked when building single documentation archives (see the `Dockerfile` of each branch) |
|
||||
| [`Dockerfile.archives`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/Dockerfile.archives) | `gitlab-docs:archives` | Contains all the versions of the website in one archive. It copies all generated HTML files from every version in one location. |
|
||||
| [`bootstrap.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/bootstrap.Dockerfile) | `gitlab-docs:bootstrap` | Contains all the dependencies that are needed to build the website. If the gems are updated and `Gemfile{,.lock}` changes, the image must be rebuilt. |
|
||||
| [`builder.onbuild.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/builder.onbuild.Dockerfile) | `gitlab-docs:builder-onbuild` | Base image to build the docs website. It uses `ONBUILD` to perform all steps and depends on `gitlab-docs:bootstrap`. |
|
||||
| [`nginx.onbuild.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/nginx.onbuild.Dockerfile) | `gitlab-docs:nginx-onbuild` | Base image to use for building documentation archives. It uses `ONBUILD` to perform all required steps to copy the archive, and relies upon its parent `Dockerfile.builder.onbuild` that is invoked when building single documentation archives (see the `Dockerfile` of each branch) |
|
||||
| [`archives.Dockerfile`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/archives.Dockerfile) | `gitlab-docs:archives` | Contains all the versions of the website in one archive. It copies all generated HTML files from every version in one location. |
|
||||
|
||||
### How to build the images
|
||||
|
||||
|
|
|
@ -1116,6 +1116,36 @@ include a visual representation to help readers understand it, you can:
|
|||
an area of the screen.
|
||||
- Create a short video of the interaction and link to it.
|
||||
|
||||
## Emojis
|
||||
|
||||
Don't use the Markdown emoji format, for example `:smile:`, for any purpose. Use
|
||||
[GitLab SVG icons](#gitlab-svg-icons) instead.
|
||||
|
||||
Use of emoji in Markdown requires GitLab Flavored Markdown, which is not supported by Kramdown,
|
||||
the Markdown rendering engine used for GitLab documentation.
|
||||
|
||||
## GitLab SVG icons
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-docs/-/issues/384) in GitLab 12.7.
|
||||
|
||||
You can use icons from the [GitLab SVG library](https://gitlab-org.gitlab.io/gitlab-svgs/)
|
||||
directly in the documentation. For example, `**{tanuki}**` renders as: **{tanuki}**.
|
||||
|
||||
In most cases, you should avoid using the icons in text.
|
||||
However, you can use an icon when hover text is the only
|
||||
available way to describe a UI element. For example, **Delete** or **Edit** buttons
|
||||
often have hover text only.
|
||||
|
||||
When you do use an icon, start with the hover text and follow it with the SVG reference in parentheses.
|
||||
|
||||
- Avoid: `Select **{pencil}** **Edit**.` This generates as: Select **{pencil}** **Edit**.
|
||||
- Use instead: `Select **Edit** (**{pencil}**).` This generates as: Select **Edit** (**{pencil}**).
|
||||
|
||||
Do not use words to describe the icon:
|
||||
|
||||
- Avoid: `Select **Erase job log** (the trash icon).`
|
||||
- Use instead: `Select **Erase job log** (**{remove}**).` This generates as: Select **Erase job log** (**{remove}**).
|
||||
|
||||
## Videos
|
||||
|
||||
Adding GitLab YouTube video tutorials to the documentation is highly
|
||||
|
@ -1197,28 +1227,6 @@ different mobile devices.
|
|||
`/help`, because the GitLab Markdown processor doesn't support iframes. It's
|
||||
hidden on the documentation site, but is displayed by `/help`.
|
||||
|
||||
## GitLab SVG icons
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-docs/-/issues/384) in GitLab 12.7.
|
||||
|
||||
You can use icons from the [GitLab SVG library](https://gitlab-org.gitlab.io/gitlab-svgs/)
|
||||
directly in the documentation. For example, `**{tanuki}**` renders as: **{tanuki}**.
|
||||
|
||||
In most cases, you should avoid using the icons in text.
|
||||
However, you can use an icon when hover text is the only
|
||||
available way to describe a UI element. For example, **Delete** or **Edit** buttons
|
||||
often have hover text only.
|
||||
|
||||
When you do use an icon, start with the hover text and follow it with the SVG reference in parentheses.
|
||||
|
||||
- Avoid: `Select **{pencil}** **Edit**.` This generates as: Select **{pencil}** **Edit**.
|
||||
- Use instead: `Select **Edit** (**{pencil}**).` This generates as: Select **Edit** (**{pencil}**).
|
||||
|
||||
Do not use words to describe the icon:
|
||||
|
||||
- Avoid: `Select **Erase job log** (the trash icon).`
|
||||
- Use instead: `Select **Erase job log** (**{remove}**).` This generates as: Select **Erase job log** (**{remove}**).
|
||||
|
||||
## Alert boxes
|
||||
|
||||
Use alert boxes to call attention to information. Use them sparingly, and never have an alert box immediately follow another alert box.
|
||||
|
|
|
@ -24,6 +24,20 @@ integration are logs. The most relevant logs for this integration are:
|
|||
|
||||
Here are some common pitfalls and how to overcome them.
|
||||
|
||||
## Common terminology
|
||||
|
||||
- **Lucene**: A full-text search library written in Java.
|
||||
- **Near real time (NRT)**: Refers to the slight latency from the time to index a
|
||||
document to the time when it becomes searchable.
|
||||
- **Cluster**: A collection of one or more nodes that work together to hold all
|
||||
the data, providing indexing and search capabilities.
|
||||
- **Node**: A single server that works as part of a cluster.
|
||||
- **Index**: A collection of documents that have somewhat similar characteristics.
|
||||
- **Document**: A basic unit of information that can be indexed.
|
||||
- **Shards**: Fully-functional and independent subdivisions of indices. Each shard is actually
|
||||
a Lucene index.
|
||||
- **Replicas**: Failover mechanisms that duplicate indices.
|
||||
|
||||
## How can I verify that my GitLab instance is using Elasticsearch?
|
||||
|
||||
There are a couple of ways to achieve that:
|
||||
|
|
|
@ -700,101 +700,21 @@ The results are stored in `gl-container-scanning-report.json`.
|
|||
|
||||
## Reports JSON format
|
||||
|
||||
The container scanning tool emits a JSON report file. For more information, see the
|
||||
[schema for this report](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/container-scanning-report-format.json).
|
||||
The container scanning tool emits JSON reports which the [GitLab Runner](https://docs.gitlab.com/runner/)
|
||||
recognizes through the [`artifacts:reports`](../../../ci/yaml/#artifactsreports)
|
||||
keyword in the CI configuration file.
|
||||
|
||||
Here's an example container scanning report:
|
||||
Once the CI job finishes, the Runner uploads these reports to GitLab, which are then available in
|
||||
the CI Job artifacts. In GitLab Ultimate, these reports can be viewed in the corresponding [pipeline](../vulnerability_report/pipeline.md)
|
||||
and become part of the [Vulnerability Report](../vulnerability_report/).
|
||||
|
||||
```json-doc
|
||||
{
|
||||
"version": "14.0.0",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"id": "df52bc8ce9a2ae56bbcb0c4ecda62123fbd6f69b",
|
||||
"category": "container_scanning",
|
||||
"message": "CVE-2019-3462 in apt-1.4.8",
|
||||
"description": "Incorrect sanitation of the 302 redirect field in HTTP transport method of apt versions 1.4.8 and earlier can lead to content injection by a MITM attacker, potentially leading to remote code execution on the target machine.",
|
||||
"severity": "High",
|
||||
"confidence": "Unknown",
|
||||
"solution": "Upgrade apt from 1.4.8 to 1.4.9",
|
||||
"scanner": {
|
||||
"id": "trivy",
|
||||
"name": "trivy"
|
||||
},
|
||||
"location": {
|
||||
"dependency": {
|
||||
"package": {
|
||||
"name": "apt"
|
||||
},
|
||||
"version": "1.4.8"
|
||||
},
|
||||
"operating_system": "debian:9.4",
|
||||
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
|
||||
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
|
||||
},
|
||||
"identifiers": [
|
||||
{
|
||||
"type": "cve",
|
||||
"name": "CVE-2019-3462",
|
||||
"value": "CVE-2019-3462",
|
||||
"url": "http://www.securityfocus.com/bid/106690"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"url": "http://www.securityfocus.com/bid/106690"
|
||||
},
|
||||
{
|
||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3462"
|
||||
},
|
||||
{
|
||||
"url": "https://lists.apache.org/thread.html/8338a0f605bdbb3a6098bb76f666a95fc2b2f53f37fa1ecc89f1146f@%3Cdevnull.infra.apache.org%3E"
|
||||
},
|
||||
{
|
||||
"url": "https://lists.debian.org/debian-lts-announce/2019/01/msg00013.html"
|
||||
},
|
||||
{
|
||||
"url": "https://lists.debian.org/debian-lts-announce/2019/01/msg00014.html"
|
||||
},
|
||||
{
|
||||
"url": "https://security.netapp.com/advisory/ntap-20190125-0002/"
|
||||
},
|
||||
{
|
||||
"url": "https://usn.ubuntu.com/3863-1/"
|
||||
},
|
||||
{
|
||||
"url": "https://usn.ubuntu.com/3863-2/"
|
||||
},
|
||||
{
|
||||
"url": "https://usn.ubuntu.com/usn/usn-3863-1"
|
||||
},
|
||||
{
|
||||
"url": "https://usn.ubuntu.com/usn/usn-3863-2"
|
||||
},
|
||||
{
|
||||
"url": "https://www.debian.org/security/2019/dsa-4371"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"remediations": []
|
||||
"scan": {
|
||||
"scanner": {
|
||||
"id": "trivy",
|
||||
"name": "Trivy",
|
||||
"url": "https://github.com/aquasecurity/trivy/",
|
||||
"vendor": {
|
||||
"name": "GitLab"
|
||||
},
|
||||
"version": "0.16.0"
|
||||
},
|
||||
"type": "container_scanning",
|
||||
"start_time": "2021-04-14T19:45:58",
|
||||
"end_time": "2021-04-14T19:46:18",
|
||||
"status": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
These reports must follow a format defined in the
|
||||
[security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas/). See:
|
||||
|
||||
- [Latest schema for the container scanning report](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/container-scanning-report-format.json).
|
||||
- [Example container scanning report](https://gitlab.com/gitlab-examples/security/security-reports/-/blob/master/samples/container-scanning.json)
|
||||
|
||||
For more information, see [Security scanner integration](../../../development/integrations/secure.md).
|
||||
|
||||
## Security Dashboard
|
||||
|
||||
|
|
|
@ -41349,6 +41349,9 @@ msgstr ""
|
|||
msgid "Unauthenticated web rate limit period in seconds"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unban"
|
||||
msgstr ""
|
||||
|
||||
msgid "Uncommitted changes will be lost if you change branches. Do you want to continue?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -42167,6 +42170,9 @@ msgstr ""
|
|||
msgid "User was successfully removed from project."
|
||||
msgstr ""
|
||||
|
||||
msgid "User was successfully unbanned."
|
||||
msgstr ""
|
||||
|
||||
msgid "User was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
"dateformat": "^5.0.1",
|
||||
"deckar01-task_list": "^2.3.1",
|
||||
"diff": "^3.4.0",
|
||||
"dompurify": "^2.3.9",
|
||||
"dompurify": "^2.3.10",
|
||||
"dropzone": "^4.2.0",
|
||||
"editorconfig": "^0.15.3",
|
||||
"emoji-regex": "^10.0.0",
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe GroupsHelper do
|
||||
include ApplicationHelper
|
||||
include AvatarsHelper
|
||||
|
||||
describe '#group_icon_url' do
|
||||
it 'returns an url for the avatar' do
|
||||
|
@ -135,6 +136,37 @@ RSpec.describe GroupsHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#group_title_link' do
|
||||
let_it_be(:group) { create(:group, :with_avatar) }
|
||||
|
||||
let(:raw_link) { group_title_link(group, show_avatar: true) }
|
||||
let(:document) { Nokogiri::HTML.parse(raw_link) }
|
||||
|
||||
describe 'link' do
|
||||
subject(:link) { document.css('.group-path').first }
|
||||
|
||||
it 'uses the group name as innerText' do
|
||||
expect(link.inner_text).to eq(group.name)
|
||||
end
|
||||
|
||||
it 'links to the group path' do
|
||||
expect(link.attr('href')).to eq(group_path(group))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'icon' do
|
||||
subject(:icon) { document.css('.avatar-tile').first }
|
||||
|
||||
it 'specifies the group name as the alt text' do
|
||||
expect(icon.attr('alt')).to eq(group.name)
|
||||
end
|
||||
|
||||
it 'uses the group\'s avatar_url' do
|
||||
expect(icon.attr('src')).to eq(group.avatar_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#share_with_group_lock_help_text' do
|
||||
context 'traversal queries' do
|
||||
let_it_be_with_reload(:root_group) { create(:group) }
|
||||
|
|
|
@ -87,7 +87,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
|
||||
context 'when IP is already banned' do
|
||||
subject { gl_auth.find_for_git_client('username', 'password', project: nil, ip: 'ip') }
|
||||
subject { gl_auth.find_for_git_client('username-does-not-matter', 'password-does-not-matter', project: nil, ip: 'ip') }
|
||||
|
||||
before do
|
||||
expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
|
||||
|
@ -219,16 +219,16 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
|
||||
it 'recognizes master passwords' do
|
||||
user = create(:user, password: 'password')
|
||||
user = create(:user)
|
||||
|
||||
expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities)
|
||||
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip')).to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities)
|
||||
end
|
||||
|
||||
include_examples 'user login operation with unique ip limit' do
|
||||
let(:user) { create(:user, password: 'password') }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
def operation
|
||||
expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities)
|
||||
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip')).to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -502,8 +502,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
user = create(
|
||||
:user,
|
||||
:blocked,
|
||||
username: 'normal_user',
|
||||
password: 'my-secret'
|
||||
username: 'normal_user'
|
||||
)
|
||||
|
||||
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
|
||||
|
@ -512,7 +511,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
|
||||
context 'when 2fa is enabled globally' do
|
||||
let_it_be(:user) do
|
||||
create(:user, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago)
|
||||
create(:user, username: 'normal_user', otp_grace_period_started_at: 1.day.ago)
|
||||
end
|
||||
|
||||
before do
|
||||
|
@ -536,7 +535,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
|
||||
context 'when 2fa is enabled personally' do
|
||||
let(:user) do
|
||||
create(:user, :two_factor, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago)
|
||||
create(:user, :two_factor, username: 'normal_user', otp_grace_period_started_at: 1.day.ago)
|
||||
end
|
||||
|
||||
it 'fails' do
|
||||
|
@ -548,8 +547,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
it 'goes through lfs authentication' do
|
||||
user = create(
|
||||
:user,
|
||||
username: 'normal_user',
|
||||
password: 'my-secret'
|
||||
username: 'normal_user'
|
||||
)
|
||||
|
||||
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
|
||||
|
@ -559,8 +557,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
it 'goes through oauth authentication when the username is oauth2' do
|
||||
user = create(
|
||||
:user,
|
||||
username: 'oauth2',
|
||||
password: 'my-secret'
|
||||
username: 'oauth2'
|
||||
)
|
||||
|
||||
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
|
||||
|
@ -635,7 +632,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
|
||||
context 'when deploy token and user have the same username' do
|
||||
let(:username) { 'normal_user' }
|
||||
let(:user) { create(:user, username: username, password: 'my-secret') }
|
||||
let(:user) { create(:user, username: username) }
|
||||
let(:deploy_token) { create(:deploy_token, username: username, read_registry: false, projects: [project]) }
|
||||
|
||||
it 'succeeds for the token' do
|
||||
|
@ -648,7 +645,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
it 'succeeds for the user' do
|
||||
auth_success = { actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities }
|
||||
|
||||
expect(gl_auth.find_for_git_client(username, 'my-secret', project: project, ip: 'ip'))
|
||||
expect(gl_auth.find_for_git_client(username, user.password, project: project, ip: 'ip'))
|
||||
.to have_attributes(auth_success)
|
||||
end
|
||||
end
|
||||
|
@ -834,72 +831,64 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
|
||||
describe 'find_with_user_password' do
|
||||
let!(:user) do
|
||||
create(:user,
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirmation: password)
|
||||
end
|
||||
|
||||
let!(:user) { create(:user, username: username) }
|
||||
let(:username) { 'John' } # username isn't lowercase, test this
|
||||
let(:password) { 'my-secret' }
|
||||
|
||||
it "finds user by valid login/password" do
|
||||
expect(gl_auth.find_with_user_password(username, password)).to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to eql user
|
||||
end
|
||||
|
||||
it 'finds user by valid email/password with case-insensitive email' do
|
||||
expect(gl_auth.find_with_user_password(user.email.upcase, password)).to eql user
|
||||
expect(gl_auth.find_with_user_password(user.email.upcase, user.password)).to eql user
|
||||
end
|
||||
|
||||
it 'finds user by valid username/password with case-insensitive username' do
|
||||
expect(gl_auth.find_with_user_password(username.upcase, password)).to eql user
|
||||
expect(gl_auth.find_with_user_password(username.upcase, user.password)).to eql user
|
||||
end
|
||||
|
||||
it "does not find user with invalid password" do
|
||||
password = 'wrong'
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
expect(gl_auth.find_with_user_password(username, 'incorrect_password')).not_to eql user
|
||||
end
|
||||
|
||||
it "does not find user with invalid login" do
|
||||
user = 'wrong'
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
username = 'wrong'
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).not_to eql user
|
||||
end
|
||||
|
||||
include_examples 'user login operation with unique ip limit' do
|
||||
def operation
|
||||
expect(gl_auth.find_with_user_password(username, password)).to eq(user)
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to eq(user)
|
||||
end
|
||||
end
|
||||
|
||||
it 'finds the user in deactivated state' do
|
||||
user.deactivate!
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to eql user
|
||||
end
|
||||
|
||||
it "does not find user in blocked state" do
|
||||
user.block
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).not_to eql user
|
||||
end
|
||||
|
||||
it 'does not find user in locked state' do
|
||||
user.lock_access!
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).not_to eql user
|
||||
end
|
||||
|
||||
it "does not find user in ldap_blocked state" do
|
||||
user.ldap_block
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).not_to eql user
|
||||
end
|
||||
|
||||
it 'does not find user in blocked_pending_approval state' do
|
||||
user.block_pending_approval
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).not_to eql user
|
||||
end
|
||||
|
||||
context 'with increment_failed_attempts' do
|
||||
|
@ -917,7 +906,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
user.save!
|
||||
|
||||
expect do
|
||||
gl_auth.find_with_user_password(username, password, increment_failed_attempts: true)
|
||||
gl_auth.find_with_user_password(username, user.password, increment_failed_attempts: true)
|
||||
user.reload
|
||||
end.to change(user, :failed_attempts).from(2).to(0)
|
||||
end
|
||||
|
@ -946,7 +935,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
user.save!
|
||||
|
||||
expect do
|
||||
gl_auth.find_with_user_password(username, password, increment_failed_attempts: true)
|
||||
gl_auth.find_with_user_password(username, user.password, increment_failed_attempts: true)
|
||||
user.reload
|
||||
end.not_to change(user, :failed_attempts)
|
||||
end
|
||||
|
@ -961,7 +950,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
it "tries to autheticate with db before ldap" do
|
||||
expect(Gitlab::Auth::Ldap::Authentication).not_to receive(:login)
|
||||
|
||||
expect(gl_auth.find_with_user_password(username, password)).to eq(user)
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to eq(user)
|
||||
end
|
||||
|
||||
it "does not find user by using ldap as fallback to for authentication" do
|
||||
|
@ -983,7 +972,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
|
||||
it "does not find user by valid login/password" do
|
||||
expect(gl_auth.find_with_user_password(username, password)).to be_nil
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to be_nil
|
||||
end
|
||||
|
||||
context "with ldap enabled" do
|
||||
|
@ -992,7 +981,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
|
||||
it "does not find non-ldap user by valid login/password" do
|
||||
expect(gl_auth.find_with_user_password(username, password)).to be_nil
|
||||
expect(gl_auth.find_with_user_password(username, user.password)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,6 +34,8 @@ RSpec.describe 'getting a detailed sentry error' do
|
|||
|
||||
context 'when data is loading via reactive cache' do
|
||||
before do
|
||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
|
||||
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
|
@ -48,6 +50,10 @@ RSpec.describe 'getting a detailed sentry error' do
|
|||
.to receive(:issue_details)
|
||||
.and_return({ issue: sentry_detailed_error })
|
||||
|
||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
|
||||
.to receive(:track_event)
|
||||
.with('error_tracking_view_details', values: current_user.id)
|
||||
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
|
|
|
@ -16,9 +16,16 @@ RSpec.describe 'merge requests discussions' do
|
|||
login_as(user)
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/InstanceVariable
|
||||
def send_request
|
||||
get discussions_namespace_project_merge_request_path(namespace_id: project.namespace, project_id: project, id: merge_request.iid)
|
||||
get(
|
||||
discussions_namespace_project_merge_request_path(namespace_id: project.namespace, project_id: project, id: merge_request.iid),
|
||||
headers: { 'If-None-Match' => @etag }
|
||||
)
|
||||
|
||||
@etag = response.etag
|
||||
end
|
||||
# rubocop:enable RSpec/InstanceVariable
|
||||
|
||||
it 'returns 200' do
|
||||
send_request
|
||||
|
@ -63,11 +70,6 @@ RSpec.describe 'merge requests discussions' do
|
|||
let!(:award_emoji) { create(:award_emoji, awardable: first_note) }
|
||||
let!(:author_membership) { project.add_maintainer(author) }
|
||||
|
||||
before do
|
||||
# Make a request to cache the discussions
|
||||
send_request
|
||||
end
|
||||
|
||||
shared_examples 'cache miss' do
|
||||
it 'does not hit a warm cache' do
|
||||
expect_next_instance_of(DiscussionSerializer) do |serializer|
|
||||
|
@ -80,176 +82,213 @@ RSpec.describe 'merge requests discussions' do
|
|||
end
|
||||
end
|
||||
|
||||
it 'gets cached on subsequent requests' do
|
||||
expect_next_instance_of(DiscussionSerializer) do |serializer|
|
||||
expect(serializer).not_to receive(:represent)
|
||||
end
|
||||
shared_examples 'cache hit' do
|
||||
it 'gets cached on subsequent requests' do
|
||||
expect_next_instance_of(DiscussionSerializer) do |serializer|
|
||||
expect(serializer).not_to receive(:represent)
|
||||
end
|
||||
|
||||
send_request
|
||||
end
|
||||
|
||||
context 'when a note in a discussion got updated' do
|
||||
before do
|
||||
first_note.update!(updated_at: 1.minute.from_now)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
send_request
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a note in a discussion got its reference state updated' do
|
||||
context 'when mr_discussions_http_cache and disabled_mr_discussions_redis_cache are enabled' do
|
||||
before do
|
||||
reference.close!
|
||||
send_request
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
it_behaves_like 'cache hit'
|
||||
|
||||
context 'when a note in a discussion got resolved' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
first_note.resolve!(user)
|
||||
context 'when a note in a discussion got updated' do
|
||||
before do
|
||||
first_note.update!(updated_at: 1.minute.from_now)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
context 'when a note in a discussion got its reference state updated' do
|
||||
before do
|
||||
reference.close!
|
||||
end
|
||||
|
||||
context 'when a note is added to a discussion' do
|
||||
let!(:third_note) { create(:diff_note_on_merge_request, in_reply_to: first_note, noteable: merge_request, project: project) }
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note, third_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a note is removed from a discussion' do
|
||||
before do
|
||||
second_note.destroy!
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an emoji is awarded to a note in discussion' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
create(:award_emoji, awardable: first_note)
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
context 'when a note in a discussion got resolved' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
first_note.resolve!(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an award emoji is removed from a note in discussion' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
award_emoji.destroy!
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
context 'when a note is added to a discussion' do
|
||||
let!(:third_note) { create(:diff_note_on_merge_request, in_reply_to: first_note, noteable: merge_request, project: project) }
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note, third_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a note is removed from a discussion' do
|
||||
before do
|
||||
second_note.destroy!
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an emoji is awarded to a note in discussion' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
create(:award_emoji, awardable: first_note)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an award emoji is removed from a note in discussion' do
|
||||
before do
|
||||
travel_to(1.minute.from_now) do
|
||||
award_emoji.destroy!
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the diff note position changes' do
|
||||
before do
|
||||
# This replicates a position change wherein timestamps aren't updated
|
||||
# which is why `Gitlab::Timeless.timeless` is utilized. This is the
|
||||
# same approach being used in Discussions::UpdateDiffPositionService
|
||||
# which is responsible for updating the positions of diff discussions
|
||||
# when MR updates.
|
||||
first_note.position = Gitlab::Diff::Position.new(
|
||||
old_path: first_note.position.old_path,
|
||||
new_path: first_note.position.new_path,
|
||||
old_line: first_note.position.old_line,
|
||||
new_line: first_note.position.new_line + 1,
|
||||
diff_refs: first_note.position.diff_refs
|
||||
)
|
||||
|
||||
Gitlab::Timeless.timeless(first_note, &:save)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the HEAD diff note position changes' do
|
||||
before do
|
||||
# This replicates a DiffNotePosition change. This is the same approach
|
||||
# being used in Discussions::CaptureDiffNotePositionService which is
|
||||
# responsible for updating/creating DiffNotePosition of a diff discussions
|
||||
# in relation to HEAD diff.
|
||||
new_position = Gitlab::Diff::Position.new(
|
||||
old_path: first_note.position.old_path,
|
||||
new_path: first_note.position.new_path,
|
||||
old_line: first_note.position.old_line,
|
||||
new_line: first_note.position.new_line + 1,
|
||||
diff_refs: first_note.position.diff_refs
|
||||
)
|
||||
|
||||
DiffNotePosition.create_or_update_for(
|
||||
first_note,
|
||||
diff_type: :head,
|
||||
position: new_position,
|
||||
line_code: 'bd4b7bfff3a247ccf6e3371c41ec018a55230bcc_534_521'
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author detail changes' do
|
||||
before do
|
||||
author.update!(name: "#{author.name} (Updated)")
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author status changes' do
|
||||
before do
|
||||
Users::SetStatusService.new(author, message: "updated status").execute
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author role changes' do
|
||||
before do
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(author_membership)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user role changes' do
|
||||
before do
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.member(user))
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the diff note position changes' do
|
||||
context 'when mr_discussions_http_cache is disabled' do
|
||||
before do
|
||||
# This replicates a position change wherein timestamps aren't updated
|
||||
# which is why `Gitlab::Timeless.timeless` is utilized. This is the
|
||||
# same approach being used in Discussions::UpdateDiffPositionService
|
||||
# which is responsible for updating the positions of diff discussions
|
||||
# when MR updates.
|
||||
first_note.position = Gitlab::Diff::Position.new(
|
||||
old_path: first_note.position.old_path,
|
||||
new_path: first_note.position.new_path,
|
||||
old_line: first_note.position.old_line,
|
||||
new_line: first_note.position.new_line + 1,
|
||||
diff_refs: first_note.position.diff_refs
|
||||
)
|
||||
|
||||
Gitlab::Timeless.timeless(first_note, &:save)
|
||||
stub_feature_flags(mr_discussions_http_cache: false)
|
||||
send_request
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
it_behaves_like 'cache hit'
|
||||
end
|
||||
|
||||
context 'when the HEAD diff note position changes' do
|
||||
context 'when disabled_mr_discussions_redis_cache is disabled' do
|
||||
before do
|
||||
# This replicates a DiffNotePosition change. This is the same approach
|
||||
# being used in Discussions::CaptureDiffNotePositionService which is
|
||||
# responsible for updating/creating DiffNotePosition of a diff discussions
|
||||
# in relation to HEAD diff.
|
||||
new_position = Gitlab::Diff::Position.new(
|
||||
old_path: first_note.position.old_path,
|
||||
new_path: first_note.position.new_path,
|
||||
old_line: first_note.position.old_line,
|
||||
new_line: first_note.position.new_line + 1,
|
||||
diff_refs: first_note.position.diff_refs
|
||||
)
|
||||
|
||||
DiffNotePosition.create_or_update_for(
|
||||
first_note,
|
||||
diff_type: :head,
|
||||
position: new_position,
|
||||
line_code: 'bd4b7bfff3a247ccf6e3371c41ec018a55230bcc_534_521'
|
||||
)
|
||||
stub_feature_flags(disabled_mr_discussions_redis_cache: false)
|
||||
send_request
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
it_behaves_like 'cache hit'
|
||||
end
|
||||
|
||||
context 'when author detail changes' do
|
||||
context 'when mr_discussions_http_cache and disabled_mr_discussions_redis_cache are disabled' do
|
||||
before do
|
||||
author.update!(name: "#{author.name} (Updated)")
|
||||
stub_feature_flags(mr_discussions_http_cache: false, disabled_mr_discussions_redis_cache: false)
|
||||
send_request
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author status changes' do
|
||||
before do
|
||||
Users::SetStatusService.new(author, message: "updated status").execute
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author role changes' do
|
||||
before do
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(author_membership)
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user role changes' do
|
||||
before do
|
||||
Members::UpdateService.new(owner, access_level: Gitlab::Access::GUEST).execute(project.member(user))
|
||||
end
|
||||
|
||||
it_behaves_like 'cache miss' do
|
||||
let(:changed_notes) { [first_note, second_note] }
|
||||
end
|
||||
it_behaves_like 'cache hit'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9001,9 +9001,9 @@ tapable@^1.0.0, tapable@^1.1.3:
|
|||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tar@^6.0.2:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83"
|
||||
integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
dependencies:
|
||||
chownr "^2.0.0"
|
||||
fs-minipass "^2.0.0"
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -5076,10 +5076,10 @@ dompurify@2.3.6:
|
|||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
|
||||
integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
|
||||
|
||||
dompurify@^2.3.9:
|
||||
version "2.3.9"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.9.tgz#a4be5e7278338d6db09922dffcf6182cd099d70a"
|
||||
integrity sha512-3zOnuTwup4lPV/GfGS6UzG4ub9nhSYagR/5tB3AvDEwqyy5dtyCM2dVjwGDCnrPerXifBKTYh/UWCGKK7ydhhw==
|
||||
dompurify@^2.3.10, dompurify@^2.3.9:
|
||||
version "2.3.10"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385"
|
||||
integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g==
|
||||
|
||||
domutils@^2.5.2, domutils@^2.6.0:
|
||||
version "2.6.0"
|
||||
|
@ -12007,9 +12007,9 @@ tapable@^1.0.0, tapable@^1.1.3:
|
|||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tar@^6.0.2:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f"
|
||||
integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
dependencies:
|
||||
chownr "^2.0.0"
|
||||
fs-minipass "^2.0.0"
|
||||
|
|
Loading…
Reference in a new issue