Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-09 00:13:41 +00:00
parent 52af12cfec
commit 386339e38e
34 changed files with 426 additions and 113 deletions

View File

@ -1 +1 @@
034cc7332fc1ebf67599f7f9e98e1588bc6d1823
4ba8618078d9107d52c0d735f76286ab0b113a8a

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Resolvers
module Users
class ParticipantsResolver < BaseResolver
type Types::UserType.connection_type, null: true
def resolve(**args)
object.visible_participants(current_user)
end
end
end
end

View File

@ -80,7 +80,8 @@ module Types
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :participants, Types::UserType.connection_type, null: true, complexity: 5,
description: 'List of participants in the issue.'
description: 'List of participants in the issue.',
resolver: Resolvers::Users::ParticipantsResolver
field :emails_disabled, GraphQL::Types::Boolean, null: false,
method: :project_emails_disabled?,
description: 'Indicates if a project has email notifications disabled: `true` if email notifications are disabled.'

View File

@ -148,7 +148,8 @@ module Types
field :author, Types::UserType, null: true,
description: 'User who created this merge request.'
field :participants, Types::UserType.connection_type, null: true, complexity: 15,
description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.'
description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.',
resolver: Resolvers::Users::ParticipantsResolver
field :subscribed, GraphQL::Types::Boolean, method: :subscribed?, null: false, complexity: 5,
description: 'Indicates if the currently logged in user is subscribed to this merge request.'
field :labels, Types::LabelType.connection_type, null: true, complexity: 5,

View File

@ -21,7 +21,7 @@ class ApplicationSetting < ApplicationRecord
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
add_authentication_token_field :health_check_access_token
add_authentication_token_field :static_objects_external_storage_auth_token
add_authentication_token_field :static_objects_external_storage_auth_token, encrypted: :optional
belongs_to :self_monitoring_project, class_name: "Project", foreign_key: 'instance_administration_project_id'
belongs_to :push_rule

View File

@ -363,6 +363,14 @@ module ApplicationSettingImplementation
super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end
def static_objects_external_storage_auth_token=(token)
if token.present?
set_static_objects_external_storage_auth_token(token)
else
self.static_objects_external_storage_auth_token_encrypted = nil
end
end
def performance_bar_allowed_group
Group.find_by_id(performance_bar_allowed_group_id)
end

View File

@ -59,7 +59,7 @@ module Ci
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', inverse_of: :build
has_many :terraform_state_versions, class_name: 'Terraform::StateVersion', dependent: :nullify, inverse_of: :build, foreign_key: :ci_build_id # rubocop:disable Cop/ActiveRecordDependent
has_many :terraform_state_versions, class_name: 'Terraform::StateVersion', inverse_of: :build, foreign_key: :ci_build_id
accepts_nested_attributes_for :runner_session, update_only: true
accepts_nested_attributes_for :job_variables

View File

@ -60,6 +60,15 @@ module Participable
filtered_participants_hash[user]
end
# Returns only participants visible for the user
#
# Returns an Array of User instances.
def visible_participants(user)
return participants(user) unless Feature.enabled?(:verify_participants_access, project, default_enabled: :yaml)
filter_by_ability(raw_participants(user, verify_access: true))
end
# Checks if the user is a participant in a discussion.
#
# This method processes attributes of objects in breadth-first order.
@ -84,8 +93,7 @@ module Participable
end
end
def raw_participants(current_user = nil)
current_user ||= author
def raw_participants(current_user = nil, verify_access: false)
ext = Gitlab::ReferenceExtractor.new(project, current_user)
participants = Set.new
process = [self]
@ -97,6 +105,8 @@ module Participable
when User
participants << source
when Participable
next unless !verify_access || source_visible_to_user?(source, current_user)
source.class.participant_attrs.each do |attr|
if attr.respond_to?(:call)
source.instance_exec(current_user, ext, &attr)
@ -116,6 +126,10 @@ module Participable
participants.merge(ext.users)
end
def source_visible_to_user?(source, user)
Ability.allowed?(user, "read_#{source.model_name.element}".to_sym, source)
end
def filter_by_ability(participants)
case self
when PersonalSnippet

View File

@ -14,7 +14,7 @@ module Ci
Ci::JobVariable.bulk_insert!(variables)
success
rescue SizeLimitError, ParserError, ActiveRecord::RecordInvalid => error
rescue SizeLimitError, ParserError, ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => error
Gitlab::ErrorTracking.track_exception(error, job_id: artifact.job_id)
error(error.message, :bad_request)
end

View File

@ -1,42 +0,0 @@
- can_create_issue = show_new_issue_link?(@project)
- can_create_project_snippet = can?(current_user, :create_snippet, @project)
- can_push_code = can?(current_user, :push_code, @project)
- create_mr_from_new_fork = can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
- merge_project = merge_request_source_project_for_project(@project)
- show_menu = can_create_issue || can_create_project_snippet || can_push_code || create_mr_from_new_fork || merge_project
- if show_menu
.project-action-button.dropdown.inline<
%a.btn.btn-default.gl-button.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
= sprite_icon('plus', css_class: 'gl-icon')
= sprite_icon("chevron-down", css_class: 'gl-icon')
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
- if can_create_issue || merge_project || can_create_project_snippet
%li.dropdown-header= _('This project')
- if can_create_issue
%li= link_to _('New issue'), new_project_issue_path(@project)
- if merge_project
%li= link_to _('New merge request'), project_new_merge_request_path(merge_project)
- if can_create_project_snippet
%li= link_to _('New snippet'), new_project_snippet_path(@project)
- if can_push_code
%li.dropdown-header= _('This repository')
- if can_push_code
%li.qa-new-file-option= link_to _('New file'), project_new_blob_path(@project, @project.default_branch_or_main)
- unless @project.empty_repo?
%li= link_to _('New branch'), new_project_branch_path(@project)
%li= link_to _('New tag'), new_project_tag_path(@project)
- elsif can_collaborate_with_project?(@project)
%li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch_or_main)
- elsif create_mr_from_new_fork
- continue_params = { to: project_new_blob_path(@project, @project.default_branch_or_main),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
%li= link_to _('New file'), fork_path, method: :post

View File

@ -4,7 +4,7 @@
- add_page_specific_style 'page_bundles/pipeline_schedules'
%h3.page-title
= _("Edit Pipeline Schedule %{id}") % { id: @schedule.id }
= _("Edit Pipeline Schedule")
%hr
= render "form"

View File

@ -2,7 +2,7 @@
- release = @releases.find { |release| release.tag == tag.name }
- commit_status = @tag_pipeline_statuses[tag.name] unless @tag_pipeline_statuses.nil?
%li.flex-row.js-tag-list{ class: "gl-white-space-normal!" }
%li.flex-row.js-tag-list{ class: "gl-white-space-normal! gl-align-items-flex-start!" }
.row-main-content
= sprite_icon('tag')
= link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name'
@ -11,10 +11,6 @@
%span.badge.badge-success.gl-ml-2.gl-badge.sm.badge-pill
= s_('TagsPage|protected')
- if tag.message.present?
&nbsp;
= strip_signature(tag.message)
- if commit
.block-truncated
= render 'projects/branches/commit', commit: commit, project: @project
@ -28,6 +24,10 @@
= _("Release")
= link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'gl-text-blue-600!'
- if tag.message.present?
%pre.wrap
= strip_signature(tag.message)
.row-fixed-content.controls.flex-row
- if tag.has_signature?
= render partial: 'projects/commit/signature', object: tag.signature

View File

@ -0,0 +1,8 @@
---
name: verify_participants_access
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74906
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347407
milestone: '14.6'
type: development
group: group::source code
default_enabled: false

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class EncryptStaticObjectsExternalStorageAuthToken < Gitlab::Database::Migration[1.0]
class ApplicationSetting < ActiveRecord::Base
self.table_name = 'application_settings'
scope :encrypted_token_is_null, -> { where(static_objects_external_storage_auth_token_encrypted: nil) }
scope :encrypted_token_is_not_null, -> { where.not(static_objects_external_storage_auth_token_encrypted: nil) }
scope :plaintext_token_is_not_null, -> { where.not(static_objects_external_storage_auth_token: nil) }
end
def up
ApplicationSetting.reset_column_information
ApplicationSetting.encrypted_token_is_null.plaintext_token_is_not_null.find_each do |application_setting|
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(application_setting.static_objects_external_storage_auth_token)
application_setting.update!(static_objects_external_storage_auth_token_encrypted: token_encrypted)
end
end
def down
ApplicationSetting.reset_column_information
ApplicationSetting.encrypted_token_is_not_null.find_each do |application_setting|
token = Gitlab::CryptoHelper.aes256_gcm_decrypt(application_setting.static_objects_external_storage_auth_token_encrypted)
application_setting.update!(static_objects_external_storage_auth_token: token, static_objects_external_storage_auth_token_encrypted: nil)
end
end
end

View File

@ -0,0 +1 @@
2e6e432ecf7b2c885905fd4df6b57fa99b324f56cb0850d9fc792b4a9b363423

View File

@ -472,7 +472,7 @@ module API
end
get ':id/issues/:issue_iid/participants' do
issue = find_project_issue(params[:issue_iid])
participants = ::Kaminari.paginate_array(issue.participants)
participants = ::Kaminari.paginate_array(issue.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic, current_user: current_user, project: user_project
end

View File

@ -282,7 +282,7 @@ module API
get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
participants = ::Kaminari.paginate_array(merge_request.participants)
participants = ::Kaminari.paginate_array(merge_request.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic
end

View File

@ -31,6 +31,10 @@ module Gitlab
false
end
def type
nil
end
def inspect
"#<#{self.class.name}>"
end

View File

@ -41,3 +41,7 @@ pages_deployments:
- table: ci_builds
column: ci_build_id
on_delete: async_nullify
terraform_state_versions:
- table: ci_builds
column: ci_build_id
on_delete: async_nullify

View File

@ -9932,9 +9932,6 @@ msgstr ""
msgid "Create new project"
msgstr ""
msgid "Create new..."
msgstr ""
msgid "Create or import your first project"
msgstr ""
@ -12665,7 +12662,7 @@ msgstr ""
msgid "Edit Password"
msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgid "Edit Pipeline Schedule"
msgstr ""
msgid "Edit Release"
@ -38792,12 +38789,21 @@ msgstr ""
msgid "VulnerabilityManagement|%{statusStart}Resolved%{statusEnd} %{timeago} by %{user}"
msgstr ""
msgid "VulnerabilityManagement|A removed or remediated vulnerability"
msgstr ""
msgid "VulnerabilityManagement|A true-positive and will fix"
msgstr ""
msgid "VulnerabilityManagement|A verified true-positive vulnerability"
msgstr ""
msgid "VulnerabilityManagement|Add vulnerability finding"
msgstr ""
msgid "VulnerabilityManagement|An unverified non-confirmed finding"
msgstr ""
msgid "VulnerabilityManagement|Change status"
msgstr ""
@ -38825,6 +38831,9 @@ msgstr ""
msgid "VulnerabilityManagement|Requires assessment"
msgstr ""
msgid "VulnerabilityManagement|Select a method"
msgstr ""
msgid "VulnerabilityManagement|Something went wrong while trying to delete the comment. Please try again later."
msgstr ""
@ -38894,9 +38903,18 @@ msgstr ""
msgid "Vulnerability|Additional Info"
msgstr ""
msgid "Vulnerability|Bug Bounty"
msgstr ""
msgid "Vulnerability|CVSS v3"
msgstr ""
msgid "Vulnerability|Class"
msgstr ""
msgid "Vulnerability|Code Review"
msgstr ""
msgid "Vulnerability|Comments"
msgstr ""
@ -38912,21 +38930,33 @@ msgstr ""
msgid "Vulnerability|Description"
msgstr ""
msgid "Vulnerability|Details"
msgstr ""
msgid "Vulnerability|Detected"
msgstr ""
msgid "Vulnerability|Detection method"
msgstr ""
msgid "Vulnerability|Download"
msgstr ""
msgid "Vulnerability|Evidence"
msgstr ""
msgid "Vulnerability|External Security Report"
msgstr ""
msgid "Vulnerability|False positive detected"
msgstr ""
msgid "Vulnerability|File"
msgstr ""
msgid "Vulnerability|GitLab Security Report"
msgstr ""
msgid "Vulnerability|Identifier"
msgstr ""
@ -38936,6 +38966,9 @@ msgstr ""
msgid "Vulnerability|Image"
msgstr ""
msgid "Vulnerability|Information related how the vulnerability was discovered and its impact to the system."
msgstr ""
msgid "Vulnerability|Links"
msgstr ""
@ -38960,6 +38993,15 @@ msgstr ""
msgid "Vulnerability|Scanner Provider"
msgstr ""
msgid "Vulnerability|Security Audit"
msgstr ""
msgid "Vulnerability|Select a severity"
msgstr ""
msgid "Vulnerability|Set the status of the vulnerability finding based on the information available to you."
msgstr ""
msgid "Vulnerability|Severity"
msgstr ""

View File

@ -45,10 +45,6 @@ module QA
element :tree_holder, '.tree-holder' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/buttons/_dropdown.html.haml' do
element :create_new_dropdown
end
view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('ProjectOverview|Fork')" # rubocop:disable QA/ElementWithPattern
element :fork_link, "link_to new_project_fork_path(@project)" # rubocop:disable QA/ElementWithPattern

View File

@ -81,7 +81,6 @@ RSpec.describe 'Database schema' do
subscriptions: %w[user_id subscribable_id],
suggestions: %w[commit_id],
taggings: %w[tag_id taggable_id tagger_id],
terraform_state_versions: %w[ci_build_id],
timelogs: %w[user_id],
todos: %w[target_id commit_id],
uploads: %w[model_id],

View File

@ -445,6 +445,24 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.repository_storages_weighted).to eq('default' => 50)
end
context 'External storage for repository static objects' do
it 'changes Repository external storage settings' do
encrypted_token = Gitlab::CryptoHelper.aes256_gcm_encrypt('OldToken')
current_settings.update_attribute :static_objects_external_storage_auth_token_encrypted, encrypted_token
visit repository_admin_application_settings_path
page.within('.as-repository-static-objects') do
fill_in 'application_setting_static_objects_external_storage_url', with: 'http://example.com'
fill_in 'application_setting_static_objects_external_storage_auth_token', with: 'Token'
click_button 'Save changes'
end
expect(current_settings.static_objects_external_storage_url).to eq('http://example.com')
expect(current_settings.static_objects_external_storage_auth_token).to eq('Token')
end
end
end
context 'Reporting page' do

View File

@ -21,7 +21,8 @@ RSpec.describe 'Project > Tags', :js do
context 'page with tags list' do
it 'shows tag name' do
expect(page).to have_content 'v1.1.0 Version 1.1.0'
expect(page).to have_content 'v1.1.0'
expect(page).to have_content 'Version 1.1.0'
end
it 'shows tag edit button' do

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::Users::ParticipantsResolver do
include GraphqlHelpers
describe '#resolve' do
let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:note) do
create(
:note,
:confidential,
project: project,
noteable: issue,
author: create(:user)
)
end
subject(:resolved_items) { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items }
before do
project.add_guest(guest)
project.add_developer(user)
end
context 'when current user is not set' do
let(:current_user) { nil }
it 'returns only publicly visible participants for this user' do
is_expected.to match_array([issue.author])
end
end
context 'when current user does not have enough permissions' do
let(:current_user) { guest }
it 'returns only publicly visible participants for this user' do
is_expected.to match_array([issue.author])
end
end
context 'when current user has access to confidential notes' do
let(:current_user) { user }
it 'returns all participants for this user' do
is_expected.to match_array([issue.author, note.author])
end
end
end
end

View File

@ -40,4 +40,10 @@ RSpec.describe Gitlab::Config::Entry::Undefined do
expect(entry.specified?).to eq false
end
end
describe '#type' do
it 'returns nil' do
expect(entry.type).to eq nil
end
end
end

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe EncryptStaticObjectsExternalStorageAuthToken, :migration do
let(:application_settings) do
Class.new(ActiveRecord::Base) do
self.table_name = 'application_settings'
end
end
context 'when static_objects_external_storage_auth_token is not set' do
it 'does nothing' do
application_settings.create!
reversible_migration do |migration|
migration.before -> {
settings = application_settings.first
expect(settings.static_objects_external_storage_auth_token).to be_nil
expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
}
migration.after -> {
settings = application_settings.first
expect(settings.static_objects_external_storage_auth_token).to be_nil
expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
}
end
end
end
context 'when static_objects_external_storage_auth_token is set' do
it 'encrypts static_objects_external_storage_auth_token' do
settings = application_settings.create!
settings.update_column(:static_objects_external_storage_auth_token, 'Test')
reversible_migration do |migration|
migration.before -> {
settings = application_settings.first
expect(settings.static_objects_external_storage_auth_token).to eq('Test')
expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
}
migration.after -> {
settings = application_settings.first
expect(settings.static_objects_external_storage_auth_token).to eq('Test')
expect(settings.static_objects_external_storage_auth_token_encrypted).to be_present
}
end
end
end
end

View File

@ -1239,4 +1239,30 @@ RSpec.describe ApplicationSetting do
expect(subject.kroki_formats_excalidraw).to eq(true)
end
end
describe '#static_objects_external_storage_auth_token=' do
subject { setting.static_objects_external_storage_auth_token = token }
let(:token) { 'Test' }
it 'stores an encrypted version of the token' do
subject
expect(setting[:static_objects_external_storage_auth_token]).to be_nil
expect(setting[:static_objects_external_storage_auth_token_encrypted]).to be_present
expect(setting.static_objects_external_storage_auth_token).to eq('Test')
end
context 'when token is empty' do
let(:token) { '' }
it 'removes an encrypted version of the token' do
subject
expect(setting[:static_objects_external_storage_auth_token]).to be_nil
expect(setting[:static_objects_external_storage_auth_token_encrypted]).to be_nil
expect(setting.static_objects_external_storage_auth_token).to be_nil
end
end
end
end

View File

@ -29,7 +29,7 @@ RSpec.describe Ci::Build do
it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_session) }
it { is_expected.to have_one(:trace_metadata) }
it { is_expected.to have_many(:terraform_state_versions).dependent(:nullify).inverse_of(:build) }
it { is_expected.to have_many(:terraform_state_versions).inverse_of(:build) }
it { is_expected.to validate_presence_of(:ref) }

View File

@ -51,7 +51,9 @@ RSpec.describe Participable do
end
it 'supports attributes returning another Participable' do
other_model = Class.new { include Participable }
other_model = Class.new do
include Participable
end
other_model.participant(:bar)
model.participant(:foo)
@ -115,6 +117,76 @@ RSpec.describe Participable do
end
end
describe '#visible_participants' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(anything, :read_class, anything) { readable }
end
let(:readable) { true }
it 'returns the list of participants' do
model.participant(:foo)
model.participant(:bar)
user1 = build(:user)
user2 = build(:user)
user3 = build(:user)
project = build(:project, :public)
instance = model.new
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
expect(instance).to receive(:foo).and_return(user2)
expect(instance).to receive(:bar).and_return(user3)
expect(instance).to receive(:project).thrice.and_return(project)
participants = instance.visible_participants(user1)
expect(participants).to include(user2)
expect(participants).to include(user3)
end
context 'when Participable is not readable by the user' do
let(:readable) { false }
it 'does not return unavailable participants' do
model.participant(:bar)
instance = model.new
user1 = build(:user)
user2 = build(:user)
project = build(:project, :public)
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
allow(instance).to receive(:bar).and_return(user2)
expect(instance).to receive(:project).thrice.and_return(project)
expect(instance.visible_participants(user1)).to be_empty
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(verify_participants_access: false)
end
it 'returns unavailable participants' do
model.participant(:bar)
instance = model.new
user1 = build(:user)
user2 = build(:user)
project = build(:project, :public)
allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
allow(instance).to receive(:bar).and_return(user2)
expect(instance).to receive(:project).thrice.and_return(project)
expect(instance.visible_participants(user1)).to match_array([user2])
end
end
end
end
describe '#participant?' do
let(:instance) { model.new }

View File

@ -873,7 +873,7 @@ RSpec.describe API::Issues do
end
it 'returns 404 if the issue is confidential' do
post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
get api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
expect(response).to have_gitlab_http_status(:not_found)
end

View File

@ -23,6 +23,20 @@ RSpec.describe Ci::ParseDotenvArtifactService do
hash_including('key' => 'KEY2', 'value' => 'VAR2'))
end
context 'when dotenv variables are conflicting against manual variables' do
before do
create(:ci_job_variable, job: build, key: 'KEY1')
end
it 'returns an error message that there is a duplicate variable' do
subject
expect(subject[:status]).to eq(:error)
expect(subject[:message]).to include("Key (key, job_id)=(KEY1, #{build.id}) already exists.")
expect(subject[:http_status]).to eq(:bad_request)
end
end
context 'when parse error happens' do
before do
allow(service).to receive(:scan_line!) { raise described_class::ParserError, 'Invalid Format' }

View File

@ -28,4 +28,34 @@ RSpec.shared_examples 'issuable participants endpoint' do
expect(response).to have_gitlab_http_status(:not_found)
end
context 'with a confidential note' do
let!(:note) do
create(
:note,
:confidential,
project: project,
noteable: entity,
author: create(:user)
)
end
it 'returns a full list of participants' do
get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user)
expect(response).to have_gitlab_http_status(:ok)
participant_ids = json_response.map { |el| el['id'] }
expect(participant_ids).to match_array([entity.author_id, note.author_id])
end
context 'when user cannot see a confidential note' do
it 'returns a limited list of participants' do
get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", create(:user))
expect(response).to have_gitlab_http_status(:ok)
participant_ids = json_response.map { |el| el['id'] }
expect(participant_ids).to match_array([entity.author_id])
end
end
end
end

View File

@ -1,42 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'projects/buttons/_dropdown' do
let(:user) { create(:user) }
context 'user with all abilities' do
before do
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:can?).with(user, :push_code, project).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
end
context 'empty repository' do
let(:project) { create(:project, :empty_repo) }
it 'has a link to create a new file' do
render
expect(view).to render_template('projects/buttons/_dropdown')
expect(rendered).to have_link('New file')
end
it 'does not have a link to create a new branch' do
render
expect(view).to render_template('projects/buttons/_dropdown')
expect(rendered).not_to have_link('New branch')
end
it 'does not have a link to create a new tag' do
render
expect(view).to render_template('projects/buttons/_dropdown')
expect(rendered).not_to have_link('New tag')
end
end
end
end