Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-01 09:07:33 +00:00
parent 0537e77587
commit f5eabcfa0e
35 changed files with 452 additions and 64 deletions

View file

@ -26,7 +26,7 @@ export default {
</script>
<template>
<div v-show="draftsCount > 0">
<nav class="review-bar-component">
<nav class="review-bar-component" data-testid="review_bar_component">
<div
class="review-bar-content d-flex gl-justify-content-end"
data-qa-selector="review_bar_content"

View file

@ -75,6 +75,13 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
icon: 'approval',
tag: '@approved-by',
},
tokenAlternative: {
formattedKey: __('Approved-By'),
key: 'approved-by',
type: 'string',
param: 'usernames',
symbol: '@',
},
condition: [
{
url: 'approved_by_usernames[]=None',
@ -105,7 +112,11 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
const tokenPosition = 3;
IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvedBy.token]);
IssuableTokenKeys.tokenKeysWithAlternative.splice(tokenPosition, 0, ...[approvedBy.token]);
IssuableTokenKeys.tokenKeysWithAlternative.splice(
tokenPosition,
0,
...[approvedBy.token, approvedBy.tokenAlternative],
);
IssuableTokenKeys.conditions.push(...approvedBy.condition);
const environmentToken = {

View file

@ -2,13 +2,15 @@
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: $white;
z-index: $zindex-dropdown-menu;
padding: 7px 0 6px; // to keep aligned with "collapse sidebar" button on the left sidebar
border-top: 1px solid $border-color;
display: flex;
align-items: center;
width: 100%;
height: $toggle-sidebar-height;
padding-left: $contextual-sidebar-width;
padding-right: $gutter_collapsed_width;
background: $white;
border-top: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
.page-with-icon-sidebar & {

View file

@ -17,7 +17,7 @@ class Projects::ForksController < Projects::ApplicationController
feature_category :source_code_management
before_action do
push_frontend_feature_flag(:fork_project_form)
push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml)
end
def index

View file

@ -76,6 +76,7 @@ class MergeRequestsFinder < IssuableFinder
def filter_negated_items(items)
items = super(items)
items = by_negated_reviewer(items)
items = by_negated_approved_by(items)
by_negated_target_branch(items)
end
@ -119,6 +120,12 @@ class MergeRequestsFinder < IssuableFinder
end
# rubocop: enable CodeReuse/ActiveRecord
def by_negated_approved_by(items)
return items unless not_params[:approved_by_usernames]
items.not_approved_by_users_with_usernames(not_params[:approved_by_usernames])
end
def source_project_id
@source_project_id ||= params[:source_project_id].presence
end

View file

@ -64,7 +64,7 @@ module NavHelper
end
def admin_analytics_nav_links
%w(dev_ops_report)
%w(dev_ops_report usage_trends)
end
def group_issues_sub_menu_items

View file

@ -24,6 +24,19 @@ module ApprovableBase
.group(:id)
.having("COUNT(users.id) = ?", usernames.size)
end
scope :not_approved_by_users_with_usernames, -> (usernames) do
users = User.where(username: usernames).select(:id)
self_table = self.arel_table
app_table = Approval.arel_table
where(
Approval.where(approvals: { user_id: users })
.where(app_table[:merge_request_id].eq(self_table[:id]))
.select('true')
.arel.exists.not
)
end
end
class_methods do

View file

@ -1,6 +1,10 @@
# frozen_string_literal: true
class PlanLimits < ApplicationRecord
include IgnorableColumns
ignore_column :ci_max_artifact_size_running_container_scanning, remove_with: '14.3', remove_after: '2021-08-22'
LimitUndefinedError = Class.new(StandardError)
belongs_to :plan

View file

@ -22,7 +22,7 @@ module Projects
# Ensure HEAD points to the default branch in case it is not master
project.change_head(default_branch)
create_protected_branch if protect_branch?
create_protected_branch if protect_branch? && !protected_branch_exists?
end
def create_protected_branch
@ -44,6 +44,10 @@ module Projects
!ProtectedBranch.protected?(project, default_branch)
end
def protected_branch_exists?
project.protected_branches.find_by_name(default_branch).present?
end
def default_branch
project.default_branch
end

View file

@ -23,6 +23,10 @@
.home-panel-buttons.col-md-12.col-lg-6
- if current_user
.gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } }
- if current_user.admin?
= link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon gl-mt-3 gl-mr-2', title: s_('View group in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('admin')
- if @notification_setting
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top' } }
- if can_create_subgroups

View file

@ -47,6 +47,10 @@
= cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
- if current_user
- if current_user.admin?
= link_to [:admin, @project], class: 'btn gl-button btn-icon gl-align-self-start gl-py-2! gl-mr-3', title: s_('View project in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('admin')
.gl-display-flex.gl-align-items-start.gl-mr-3
- if @notification_setting
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }

View file

@ -1,6 +1,6 @@
- page_title s_("ForkProject|Fork project")
- if Feature.enabled?(:fork_project_form)
- if Feature.enabled?(:fork_project_form, @project, default_enabled: :yaml)
#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
endpoint: new_project_fork_path(@project, format: :json),
new_group_path: new_group_path,

View file

@ -37,7 +37,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:idempotent:
:tags: []
- :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range
:worker_name: AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker

View file

@ -1,15 +1,55 @@
# frozen_string_literal: true
module AuthorizedProjectUpdate
class UserRefreshFromReplicaWorker < ::AuthorizedProjectsWorker
class UserRefreshFromReplicaWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
feature_category :authentication_and_authorization
urgency :low
queue_namespace :authorized_project_update
# This job will not be deduplicated since it is marked with
# `data_consistency :delayed` and not `idempotent!`
# See https://gitlab.com/gitlab-org/gitlab/-/issues/325291
deduplicate :until_executing, including_scheduled: true
idempotent!
data_consistency :delayed
# This worker will start reading data from the replica database soon
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/333219
def perform(user_id)
user = User.find_by_id(user_id)
return unless user
if Feature.enabled?(:user_refresh_from_replica_worker_uses_replica_db)
enqueue_project_authorizations_refresh(user) if project_authorizations_needs_refresh?(user)
else
use_primary_database
user.refresh_authorized_projects(source: self.class.name)
end
end
private
def use_primary_database
if ::Gitlab::Database::LoadBalancing.enable?
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
end
end
def project_authorizations_needs_refresh?(user)
AuthorizedProjectUpdate::FindRecordsDueForRefreshService.new(user).needs_refresh?
end
def enqueue_project_authorizations_refresh(user)
with_context(user: user, related_class: current_caller_id) do
AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker.perform_async(user.id)
end
end
# We use this so that we can obtain the details of the original caller
# in the enqueued `AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker` job.
def current_caller_id
Gitlab::ApplicationContext.current_context_attribute('meta.caller_id').presence
end
end
end

View file

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321387
milestone: '13.10'
type: development
group: group::source code
default_enabled: false
default_enabled: true

View file

@ -0,0 +1,8 @@
---
name: user_refresh_from_replica_worker_uses_replica_db
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64276
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334766
milestone: '14.1'
type: development
group: group::access
default_enabled: false

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddPlanLimitsMaxSizeClusterImageScanningColumn < ActiveRecord::Migration[6.0]
def change
add_column :plan_limits, :ci_max_artifact_size_cluster_image_scanning, :integer, null: false, default: 0
end
end

View file

@ -0,0 +1 @@
b37bf7db9c00c8f54c0ccca2d418f1279e12ff7e5b71347966494dc5645eb648

View file

@ -16352,7 +16352,8 @@ CREATE TABLE plan_limits (
ci_registered_project_runners integer DEFAULT 1000 NOT NULL,
web_hook_calls integer DEFAULT 0 NOT NULL,
ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL,
ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL
ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL,
ci_max_artifact_size_cluster_image_scanning integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq

View file

@ -426,6 +426,7 @@ setting is used:
| `ci_max_artifact_size_archive` | 0 |
| `ci_max_artifact_size_browser_performance` | 0 |
| `ci_max_artifact_size_cluster_applications` | 0 |
| `ci_max_artifact_size_cluster_image_scanning` | 0 |
| `ci_max_artifact_size_cobertura` | 0 |
| `ci_max_artifact_size_codequality` | 0 |
| `ci_max_artifact_size_container_scanning` | 0 |
@ -444,7 +445,6 @@ setting is used:
| `ci_max_artifact_size_network_referee` | 0 |
| `ci_max_artifact_size_performance` | 0 |
| `ci_max_artifact_size_requirements` | 0 |
| `ci_max_artifact_size_running_container_scanning` | 0 |
| `ci_max_artifact_size_sast` | 0 |
| `ci_max_artifact_size_secret_detection` | 0 |
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |

View file

@ -12627,11 +12627,11 @@ Represents summary of a security report.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="securityreportsummaryapifuzzing"></a>`apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan. |
| <a id="securityreportsummaryclusterimagescanning"></a>`clusterImageScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `cluster_image_scanning` scan. |
| <a id="securityreportsummarycontainerscanning"></a>`containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan. |
| <a id="securityreportsummarycoveragefuzzing"></a>`coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan. |
| <a id="securityreportsummarydast"></a>`dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan. |
| <a id="securityreportsummarydependencyscanning"></a>`dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan. |
| <a id="securityreportsummaryrunningcontainerscanning"></a>`runningContainerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `running_container_scanning` scan. |
| <a id="securityreportsummarysast"></a>`sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan. |
| <a id="securityreportsummarysecretdetection"></a>`secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan. |
@ -13486,7 +13486,7 @@ Represents a vulnerability.
| <a id="vulnerabilitynotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| <a id="vulnerabilityprimaryidentifier"></a>`primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. |
| <a id="vulnerabilityproject"></a>`project` | [`Project`](#project) | The project on which the vulnerability was found. |
| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, RUNNING_CONTAINER_SCANNING). `Scan Type` in the UI. |
| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, CLUSTER_IMAGE_SCANNING). `Scan Type` in the UI. |
| <a id="vulnerabilityresolvedat"></a>`resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state was changed to resolved. |
| <a id="vulnerabilityresolvedby"></a>`resolvedBy` | [`UserCore`](#usercore) | The user that resolved the vulnerability. |
| <a id="vulnerabilityresolvedondefaultbranch"></a>`resolvedOnDefaultBranch` | [`Boolean!`](#boolean) | Indicates whether the vulnerability is fixed on the default branch or not. |
@ -15162,11 +15162,11 @@ The type of the security scan that found the vulnerability.
| Value | Description |
| ----- | ----------- |
| <a id="vulnerabilityreporttypeapi_fuzzing"></a>`API_FUZZING` | |
| <a id="vulnerabilityreporttypecluster_image_scanning"></a>`CLUSTER_IMAGE_SCANNING` | |
| <a id="vulnerabilityreporttypecontainer_scanning"></a>`CONTAINER_SCANNING` | |
| <a id="vulnerabilityreporttypecoverage_fuzzing"></a>`COVERAGE_FUZZING` | |
| <a id="vulnerabilityreporttypedast"></a>`DAST` | |
| <a id="vulnerabilityreporttypedependency_scanning"></a>`DEPENDENCY_SCANNING` | |
| <a id="vulnerabilityreporttyperunning_container_scanning"></a>`RUNNING_CONTAINER_SCANNING` | |
| <a id="vulnerabilityreporttypesast"></a>`SAST` | |
| <a id="vulnerabilityreporttypesecret_detection"></a>`SECRET_DETECTION` | |

View file

@ -205,6 +205,30 @@ def down
end
```
**Multiple changes on the same table:**
The helper `with_lock_retries` wraps all operations into a single transaction. When you have the lock,
you should do as much as possible inside the transaction rather than trying to get another lock later.
Be careful about running long database statements within the block. The acquired locks are kept until the transaction (block) finishes and depending on the lock type, it might block other database operations.
```ruby
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
add_column :users, :full_name, :string
add_column :users, :bio, :string
end
end
def down
with_lock_retries do
remove_column :users, :full_name
remove_column :users, :bio
end
end
```
**Removing a foreign key:**
```ruby

View file

@ -17318,6 +17318,18 @@ Status: `data_available`
Tiers:
### `usage_activity_by_stage.secure.cluster_image_scanning_scans`
Counts cluster image scanning jobs
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_cluster_image_scanning_scans.yml)
Group: `group::container security`
Status: `implemented`
Tiers: `ultimate`
### `usage_activity_by_stage.secure.container_scanning_scans`
Counts container scanning jobs
@ -17366,18 +17378,6 @@ Status: `data_available`
Tiers: `ultimate`
### `usage_activity_by_stage.secure.running_container_scanning_scans`
Counts running container scanning jobs
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_running_container_scanning_scans.yml)
Group: `group::container security`
Status: `data_available`
Tiers: `ultimate`
### `usage_activity_by_stage.secure.sast_scans`
Counts sast jobs
@ -19394,6 +19394,30 @@ Status: `data_available`
Tiers: `free`
### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_pipeline`
Pipelines containing a Cluster Image Scanning job
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_cluster_image_scanning_pipeline.yml)
Group: `group::container security`
Status: `implemented`
Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_scans`
Counts cluster image scanning jobs
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_cluster_image_scanning_scans.yml)
Group: `group::container security`
Status: `implemented`
Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.container_scanning_pipeline`
Pipelines containing a Container Scanning job
@ -19490,30 +19514,6 @@ Status: `data_available`
Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.running_container_scanning_pipeline`
Pipelines containing a Running Container Scanning job
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_running_container_scanning_pipeline.yml)
Group: `group::container security`
Status: `data_available`
Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.running_container_scanning_scans`
Counts running container scanning jobs
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_running_container_scanning_scans.yml)
Group: `group::container security`
Status: `data_available`
Tiers: `ultimate`
### `usage_activity_by_stage_monthly.secure.sast_pipeline`
Counts of Pipelines that have at least 1 SAST job

View file

@ -8,7 +8,7 @@ module API
before { authenticated_as_admin! }
feature_category :continuous_integration
feature_category :pipeline_authoring
namespace 'admin' do
namespace 'ci' do

View file

@ -7,7 +7,7 @@ module API
content_type :txt, 'text/plain'
feature_category :continuous_integration
feature_category :runner
resource :runners do
desc 'Registers a new Runner' do

View file

@ -7,7 +7,7 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_project }
feature_category :continuous_integration
feature_category :pipeline_authoring
helpers Helpers::VariablesHelpers

View file

@ -35860,6 +35860,9 @@ msgstr ""
msgid "View full log"
msgstr ""
msgid "View group in admin area"
msgstr ""
msgid "View group labels"
msgstr ""
@ -35911,6 +35914,9 @@ msgstr ""
msgid "View project"
msgstr ""
msgid "View project in admin area"
msgstr ""
msgid "View project labels"
msgstr ""

View file

@ -24,7 +24,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
it 'has review bar' do
expect(page).to have_css('.review-bar-component', visible: false)
expect(page).to have_selector('[data-testid="review_bar_component"]', visible: false)
end
it 'adds draft note' do
@ -32,7 +32,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(find('.draft-note-component')).to have_content('Line is wrong')
expect(page).to have_css('.review-bar-component')
expect(page).to have_selector('[data-testid="review_bar_component"]')
expect(find('.review-bar-content .btn-confirm')).to have_content('1')
end

View file

@ -520,6 +520,44 @@ RSpec.describe MergeRequestsFinder do
end
end
context 'filtering by approved by' do
let(:params) { { approved_by_usernames: user2.username } }
before do
create(:approval, merge_request: merge_request3, user: user2)
end
it 'returns merge requests approved by that user' do
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(merge_request3)
end
context 'not filter' do
let(:params) { { not: { approved_by_usernames: user2.username } } }
it 'returns merge requests not approved by that user' do
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request4, merge_request5)
end
end
context 'when filtering by author and not approved by' do
let(:params) { { not: { approved_by_usernames: user2.username }, author_username: user.username } }
before do
merge_request4.update!(author: user2)
end
it 'returns merge requests authored by user and not approved by user2' do
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request5)
end
end
end
context 'filtering by created_at/updated_at' do
let(:new_project) { create(:project, forked_from_project: project1) }

View file

@ -59,4 +59,25 @@ RSpec.describe ApprovableBase do
end
end
end
describe '.not_approved_by_users_with_usernames' do
subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
let!(:merge_request2) { create(:merge_request) }
let!(:merge_request3) { create(:merge_request) }
let!(:merge_request4) { create(:merge_request) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
before do
create(:approval, merge_request: merge_request, user: user)
create(:approval, merge_request: merge_request2, user: user2)
create(:approval, merge_request: merge_request2, user: user3)
create(:approval, merge_request: merge_request4, user: user3)
end
it 'has the merge request that is not approved at all and not approved by either user' do
expect(subject).to contain_exactly(merge_request3, merge_request4)
end
end
end

View file

@ -185,7 +185,7 @@ RSpec.describe PlanLimits do
ci_max_artifact_size_junit
ci_max_artifact_size_sast
ci_max_artifact_size_dast
ci_max_artifact_size_running_container_scanning
ci_max_artifact_size_cluster_image_scanning
ci_max_artifact_size_codequality
ci_max_artifact_size_license_management
ci_max_artifact_size_performance

View file

@ -99,6 +99,53 @@ RSpec.describe Projects::ProtectDefaultBranchService do
.not_to have_received(:create_protected_branch)
end
end
context 'when protected branch does not exist' do
before do
allow(service)
.to receive(:protected_branch_exists?)
.and_return(false)
allow(service)
.to receive(:protect_branch?)
.and_return(true)
end
it 'changes the HEAD of the project' do
service.protect_default_branch
expect(project)
.to have_received(:change_head)
end
it 'protects the default branch' do
service.protect_default_branch
expect(service)
.to have_received(:create_protected_branch)
end
end
context 'when protected branch already exists' do
before do
allow(service)
.to receive(:protected_branch_exists?)
.and_return(true)
end
it 'changes the HEAD of the project' do
service.protect_default_branch
expect(project)
.to have_received(:change_head)
end
it 'does not protect the default branch' do
service.protect_default_branch
expect(service)
.not_to have_received(:create_protected_branch)
end
end
end
describe '#create_protected_branch' do

View file

@ -14,4 +14,30 @@ RSpec.describe 'groups/_home_panel' do
expect(rendered).to have_content("Group ID: #{group.id}")
end
context 'admin area link' do
it 'renders admin area link for admin' do
allow(view).to receive(:current_user).and_return(create(:admin))
render
expect(rendered).to have_link(href: admin_group_path(group))
end
it 'does not render admin area link for non-admin' do
allow(view).to receive(:current_user).and_return(create(:user))
render
expect(rendered).not_to have_link(href: admin_group_path(group))
end
it 'does not render admin area link for anonymous' do
allow(view).to receive(:current_user).and_return(nil)
render
expect(rendered).not_to have_link(href: admin_group_path(group))
end
end
end

View file

@ -5,6 +5,38 @@ require 'spec_helper'
RSpec.describe 'projects/_home_panel' do
include ProjectForksHelper
context 'admin area link' do
let(:project) { create(:project) }
before do
assign(:project, project)
end
it 'renders admin area link for admin' do
allow(view).to receive(:current_user).and_return(create(:admin))
render
expect(rendered).to have_link(href: admin_project_path(project))
end
it 'does not render admin area link for non-admin' do
allow(view).to receive(:current_user).and_return(create(:user))
render
expect(rendered).not_to have_link(href: admin_project_path(project))
end
it 'does not render admin area link for anonymous' do
allow(view).to receive(:current_user).and_return(nil)
render
expect(rendered).not_to have_link(href: admin_project_path(project))
end
end
context 'notifications' do
let(:project) { create(:project) }

View file

@ -3,9 +3,97 @@
require 'spec_helper'
RSpec.describe AuthorizedProjectUpdate::UserRefreshFromReplicaWorker do
let_it_be(:project) { create(:project) }
let_it_be(:user) { project.namespace.owner }
let(:execute_worker) { subject.perform(user.id) }
it 'is labeled as low urgency' do
expect(described_class.get_urgency).to eq(:low)
end
it_behaves_like "refreshes user's project authorizations"
it_behaves_like 'worker with data consistency',
described_class,
data_consistency: :delayed
describe '#perform' do
it 'checks if a project_authorization refresh is needed for the user' do
expect(AuthorizedProjectUpdate::FindRecordsDueForRefreshService).to(
receive(:new).with(user).and_call_original)
execute_worker
end
context 'when there are project authorization records due for either removal or addition for a specific user' do
before do
user.project_authorizations.delete_all
end
it 'enqueues a new project authorization update job for the user' do
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id)
execute_worker
end
context 'setting `meta.caller_id` as `meta.related_class` in the context of the newly enqueued `UserRefreshWithLowUrgencyWorker` job' do
context 'when the `UserRefreshFromReplicaWorker` job has a `caller_id` set' do
it 'sets the same `caller_id` as `related_class`' do
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do
expect(Gitlab::ApplicationContext.current).to include('meta.related_class' => 'Foo')
end
Gitlab::ApplicationContext.with_context(caller_id: 'Foo') do
execute_worker
end
end
end
context 'when the `UserRefreshFromReplicaWorker` job does not have a `caller_id` set' do
it 'does not set the value of `related_class`' do
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do
expect(Gitlab::ApplicationContext.current).not_to include('meta.related_class')
end
execute_worker
end
end
end
end
context 'when there are no additions or removals to be made to project authorizations for a specific user' do
it 'does not enqueue a new project authorization update job for the user' do
expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).not_to receive(:perform_async)
execute_worker
end
end
context 'when the feature flag `user_refresh_from_replica_worker_uses_replica_db` is disabled' do
before do
stub_feature_flags(user_refresh_from_replica_worker_uses_replica_db: false)
end
context 'when load balancing is enabled' do
before do
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
end
it 'reads from the primary database' do
expect(Gitlab::Database::LoadBalancing::Session.current)
.to receive(:use_primary!)
execute_worker
end
end
it 'calls Users::RefreshAuthorizedProjectsService' do
source = 'AuthorizedProjectUpdate::UserRefreshFromReplicaWorker'
expect_next_instance_of(Users::RefreshAuthorizedProjectsService, user, { source: source }) do |service|
expect(service).to receive(:execute)
end
execute_worker
end
end
end
end