Commit 89841ca8 authored by Dylan Griffith's avatar Dylan Griffith

Merge branch '225998-advanced-search-result-page-is-slow' into 'master'

Optimize the ES Query for confidentiality check

Closes #225998

See merge request gitlab-org/gitlab!38095
parents 85d4e14b 62aaaa1b
...@@ -5,16 +5,15 @@ module Search ...@@ -5,16 +5,15 @@ module Search
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_accessor :current_user, :params attr_accessor :current_user, :params
attr_reader :default_project_filter
def initialize(user, params) def initialize(user, params)
@current_user, @params = user, params.dup @current_user, @params = user, params.dup
@default_project_filter = true
end end
def execute def execute
Gitlab::SearchResults.new(current_user, projects, params[:search], Gitlab::SearchResults.new(current_user,
default_project_filter: default_project_filter) params[:search],
projects)
end end
def projects def projects
......
...@@ -7,13 +7,15 @@ module Search ...@@ -7,13 +7,15 @@ module Search
def initialize(user, group, params) def initialize(user, group, params)
super(user, params) super(user, params)
@default_project_filter = false
@group = group @group = group
end end
def execute def execute
Gitlab::GroupSearchResults.new( Gitlab::GroupSearchResults.new(
current_user, projects, group, params[:search], default_project_filter: default_project_filter current_user,
params[:search],
projects,
group: group
) )
end end
......
...@@ -10,9 +10,9 @@ module Search ...@@ -10,9 +10,9 @@ module Search
def execute def execute
Gitlab::ProjectSearchResults.new(current_user, Gitlab::ProjectSearchResults.new(current_user,
project,
params[:search], params[:search],
params[:repository_ref]) project: project,
repository_ref: params[:repository_ref])
end end
def scope def scope
......
...@@ -14,9 +14,8 @@ module EE ...@@ -14,9 +14,8 @@ module EE
::Gitlab::Elastic::SearchResults.new( ::Gitlab::Elastic::SearchResults.new(
current_user, current_user,
params[:search], params[:search],
elastic_projects,
projects, projects,
elastic_global public_and_internal_projects: elastic_global
) )
end end
...@@ -24,18 +23,6 @@ module EE ...@@ -24,18 +23,6 @@ module EE
nil nil
end end
def elastic_projects
strong_memoize(:elastic_projects) do
if current_user&.can_read_all_resources?
:any
elsif current_user
current_user.authorized_projects.pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
else
[]
end
end
end
def elastic_global def elastic_global
true true
end end
......
...@@ -10,11 +10,6 @@ module EE ...@@ -10,11 +10,6 @@ module EE
group group
end end
override :elastic_projects
def elastic_projects
@elastic_projects ||= projects.pluck(:id) # rubocop:disable CodeReuse/ActiveRecord
end
override :elastic_global override :elastic_global
def elastic_global def elastic_global
false false
...@@ -26,12 +21,10 @@ module EE ...@@ -26,12 +21,10 @@ module EE
::Gitlab::Elastic::GroupSearchResults.new( ::Gitlab::Elastic::GroupSearchResults.new(
current_user, current_user,
elastic_projects,
projects,
group,
params[:search], params[:search],
elastic_global, projects,
default_project_filter: default_project_filter group: group,
public_and_internal_projects: elastic_global
) )
end end
end end
......
...@@ -13,8 +13,8 @@ module EE ...@@ -13,8 +13,8 @@ module EE
::Gitlab::Elastic::ProjectSearchResults.new( ::Gitlab::Elastic::ProjectSearchResults.new(
current_user, current_user,
params[:search], params[:search],
project, project: project,
repository_ref repository_ref: repository_ref
) )
end end
......
...@@ -9,7 +9,9 @@ module EE ...@@ -9,7 +9,9 @@ module EE
def execute def execute
return super unless use_elasticsearch? return super unless use_elasticsearch?
::Gitlab::Elastic::SnippetSearchResults.new(current_user, params[:search], elastic_projects, nil, true) ::Gitlab::Elastic::SnippetSearchResults.new(current_user,
params[:search],
projects)
end end
override :elasticsearchable_scope override :elasticsearchable_scope
......
---
title: Optimize the Advanced Search query for Issues and Notes.
merge_request: 38095
author:
type: performance
...@@ -119,22 +119,20 @@ module Elastic ...@@ -119,22 +119,20 @@ module Elastic
# documents gated by that project feature - e.g., "issues". The feature's # documents gated by that project feature - e.g., "issues". The feature's
# visibility level must be taken into account. # visibility level must be taken into account.
def project_ids_query(user, project_ids, public_and_internal_projects, features = nil) def project_ids_query(user, project_ids, public_and_internal_projects, features = nil)
# When reading cross project is not allowed, only allow searching a scoped_project_ids = scoped_project_ids(user, project_ids)
# a single project, so the `:read_*` ability is only checked once.
unless Ability.allowed?(user, :read_cross_project)
project_ids = [] if project_ids.is_a?(Array) && project_ids.size > 1
end
# At least one condition must be present, so pick no projects for # At least one condition must be present, so pick no projects for
# anonymous users. # anonymous users.
# Pick private, internal and public projects the user is a member of. # Pick private, internal and public projects the user is a member of.
# Pick all private projects for admins & auditors. # Pick all private projects for admins & auditors.
conditions = pick_projects_by_membership(project_ids, user, features) conditions = pick_projects_by_membership(scoped_project_ids, user, features)
if public_and_internal_projects if public_and_internal_projects
# Skip internal projects for anonymous and external users. # Skip internal projects for anonymous and external users.
# Others are given access to all internal projects. Admins & auditors # Others are given access to all internal projects.
# get access to internal projects where the feature is private. #
# Admins & auditors get access to internal projects even
# if the feature is private.
conditions += pick_projects_by_visibility(Project::INTERNAL, user, features) if user && !user.external? conditions += pick_projects_by_visibility(Project::INTERNAL, user, features) if user && !user.external?
# All users, including anonymous, can access public projects. # All users, including anonymous, can access public projects.
...@@ -219,6 +217,33 @@ module Elastic ...@@ -219,6 +217,33 @@ module Elastic
.filter_by_feature_visibility(feature, user) .filter_by_feature_visibility(feature, user)
.pluck_primary_key .pluck_primary_key
end end
def scoped_project_ids(current_user, project_ids)
return :any if project_ids == :any
project_ids ||= []
# When reading cross project is not allowed, only allow searching a
# a single project, so the `:read_*` ability is only checked once.
unless Ability.allowed?(current_user, :read_cross_project)
return [] if project_ids.size > 1
end
project_ids
end
def authorized_project_ids(current_user, options = {})
return [] unless current_user
scoped_project_ids = scoped_project_ids(current_user, options[:project_ids])
authorized_project_ids = current_user.authorized_projects(Gitlab::Access::REPORTER).pluck_primary_key.to_set
# if the current search is limited to a subset of projects, we should do
# confidentiality check for these projects.
authorized_project_ids &= scoped_project_ids.to_set unless scoped_project_ids == :any
authorized_project_ids.to_a
end
end end
end end
end end
...@@ -10,13 +10,15 @@ module Elastic ...@@ -10,13 +10,15 @@ module Elastic
case type case type
when 'all' when 'all'
results[:blobs] = search_blob(query, page: page, per: per, options: options) results[:commits] = search_commit(query, page: page, per: per, options: options.merge(features: 'repository'))
results[:commits] = search_commit(query, page: page, per: per, options: options) results[:blobs] = search_blob(query, type: 'blob', page: page, per: per, options: options.merge(features: 'repository'))
results[:wiki_blobs] = search_blob(query, type: 'wiki_blob', page: page, per: per, options: options) results[:wiki_blobs] = search_blob(query, type: 'wiki_blob', page: page, per: per, options: options.merge(features: 'wiki'))
when 'commit' when 'commit'
results[:commits] = search_commit(query, page: page, per: per, options: options) results[:commits] = search_commit(query, page: page, per: per, options: options.merge(features: 'repository'))
when 'blob', 'wiki_blob' when 'blob'
results[type.pluralize.to_sym] = search_blob(query, type: type, page: page, per: per, options: options) results[:blobs] = search_blob(query, type: type, page: page, per: per, options: options.merge(features: 'repository'))
when 'wiki_blob'
results[:wiki_blobs] = search_blob(query, type: type, page: page, per: per, options: options.merge(features: 'wiki'))
end end
results results
...@@ -54,12 +56,17 @@ module Elastic ...@@ -54,12 +56,17 @@ module Elastic
bool_expr = Gitlab::Elastic::BoolExpr.new bool_expr = Gitlab::Elastic::BoolExpr.new
query_hash = { query_hash = {
query: { bool: bool_expr }, query: { bool: bool_expr },
size: per, size: per,
from: per * (page - 1), from: per * (page - 1),
sort: [:_score] sort: [:_score]
} }
# If there is a :current_user set in the `options`, we can assume
# we need to do a project visibility check.
#
# Note that `:current_user` might be `nil` for a anonymous user
query_hash = project_ids_filter(query_hash, options) if options.key?(:current_user)
if query.blank? if query.blank?
bool_expr[:must] = { match_all: {} } bool_expr[:must] = { match_all: {} }
query_hash[:track_scores] = true query_hash[:track_scores] = true
...@@ -73,11 +80,15 @@ module Elastic ...@@ -73,11 +80,15 @@ module Elastic
} }
end end
options_filter_context = options_filter_context(:commit, options) # add the document type filter
bool_expr[:filter] << { term: { type: 'commit' } } bool_expr[:filter] << { term: { type: 'commit' } }
# add filters extracted from the options
options_filter_context = options_filter_context(:commit, options)
bool_expr[:filter] += options_filter_context[:filter] if options_filter_context[:filter].any? bool_expr[:filter] += options_filter_context[:filter] if options_filter_context[:filter].any?
options[:order] = :default if options[:order].blank?
if options[:highlight] if options[:highlight]
es_fields = fields.map { |field| field.split('^').first }.each_with_object({}) do |field, memo| es_fields = fields.map { |field| field.split('^').first }.each_with_object({}) do |field, memo|
memo[field.to_sym] = {} memo[field.to_sym] = {}
...@@ -90,8 +101,6 @@ module Elastic ...@@ -90,8 +101,6 @@ module Elastic
} }
end end
options[:order] = :default if options[:order].blank?
res = search(query_hash, options) res = search(query_hash, options)
{ {
results: res.results, results: res.results,
...@@ -125,6 +134,12 @@ module Elastic ...@@ -125,6 +134,12 @@ module Elastic
} }
} }
# If there is a :current_user set in the `options`, we can assume
# we need to do a project visibility check.
#
# Note that `:current_user` might be `nil` for a anonymous user
query_hash = project_ids_filter(query_hash, options) if options.key?(:current_user)
# add the document type filter # add the document type filter
bool_expr[:filter] << { term: { type: type } } bool_expr[:filter] << { term: { type: type } }
......
...@@ -13,29 +13,32 @@ module Elastic ...@@ -13,29 +13,32 @@ module Elastic
options[:features] = 'issues' options[:features] = 'issues'
query_hash = project_ids_filter(query_hash, options) query_hash = project_ids_filter(query_hash, options)
query_hash = confidentiality_filter(query_hash, options[:current_user], options[:project_ids]) query_hash = confidentiality_filter(query_hash, options)
search(query_hash, options) search(query_hash, options)
end end
private private
def user_has_access_to_confidential_issues?(authorized_project_ids, project_ids) def confidentiality_filter(query_hash, options)
# is_a?(Array) is needed because we might receive project_ids: :any current_user = options[:current_user]
return false unless authorized_project_ids && project_ids.is_a?(Array) project_ids = options[:project_ids]
(project_ids - authorized_project_ids).empty?
end
def confidentiality_filter(query_hash, current_user, project_ids)
return query_hash if current_user&.can_read_all_resources? return query_hash if current_user&.can_read_all_resources?
authorized_project_ids = current_user&.authorized_projects(Gitlab::Access::REPORTER)&.pluck_primary_key scoped_project_ids = scoped_project_ids(current_user, project_ids)
return query_hash if user_has_access_to_confidential_issues?(authorized_project_ids, project_ids) authorized_project_ids = authorized_project_ids(current_user, options)
# we can shortcut the filter if the user is authorized to see
# all the projects for which this query is scoped on
unless scoped_project_ids == :any || scoped_project_ids.empty?
return query_hash if authorized_project_ids.to_set == scoped_project_ids.to_set
end
filter = filter = { term: { confidential: false } }
if current_user
{ if current_user
filter = {
bool: { bool: {
should: [ should: [
{ term: { confidential: false } }, { term: { confidential: false } },
...@@ -58,9 +61,7 @@ module Elastic ...@@ -58,9 +61,7 @@ module Elastic
] ]
} }
} }
else end
{ term: { confidential: false } }
end
query_hash[:query][:bool][:filter] << filter query_hash[:query][:bool][:filter] << filter
query_hash query_hash
......
...@@ -14,7 +14,7 @@ module Elastic ...@@ -14,7 +14,7 @@ module Elastic
query_hash = basic_query_hash(%w[note], query) query_hash = basic_query_hash(%w[note], query)
query_hash = project_ids_filter(query_hash, options) query_hash = project_ids_filter(query_hash, options)
query_hash = confidentiality_filter(query_hash, options[:current_user]) query_hash = confidentiality_filter(query_hash, options)
query_hash[:highlight] = highlight_options(options[:in]) query_hash[:highlight] = highlight_options(options[:in])
...@@ -23,7 +23,9 @@ module Elastic ...@@ -23,7 +23,9 @@ module Elastic
private private
def confidentiality_filter(query_hash, current_user) def confidentiality_filter(query_hash, options)
current_user = options[:current_user]
return query_hash if current_user&.can_read_all_resources? return query_hash if current_user&.can_read_all_resources?
filter = { filter = {
...@@ -43,7 +45,7 @@ module Elastic ...@@ -43,7 +45,7 @@ module Elastic
bool: { bool: {
should: [ should: [
{ bool: { must_not: [{ exists: { field: :confidential } }] } }, { bool: { must_not: [{ exists: { field: :confidential } }] } },
{ term: { "confidential" => false } } { term: { confidential: false } }
] ]
} }
} }
...@@ -61,7 +63,7 @@ module Elastic ...@@ -61,7 +63,7 @@ module Elastic
bool: { bool: {
should: [ should: [
{ term: { "issue.confidential" => true } }, { term: { "issue.confidential" => true } },
{ term: { "confidential" => true } } { term: { confidential: true } }
] ]
} }
}, },
...@@ -70,7 +72,7 @@ module Elastic ...@@ -70,7 +72,7 @@ module Elastic
should: [ should: [
{ term: { "issue.author_id" => current_user.id } }, { term: { "issue.author_id" => current_user.id } },
{ term: { "issue.assignee_id" => current_user.id } }, { term: { "issue.assignee_id" => current_user.id } },
{ terms: { "project_id" => current_user.authorized_projects(Gitlab::Access::REPORTER).pluck_primary_key } } { terms: { project_id: authorized_project_ids(current_user, options) } }
] ]
} }
} }
......
...@@ -11,15 +11,20 @@ module Gitlab ...@@ -11,15 +11,20 @@ module Gitlab
attr_reader :group, :default_project_filter attr_reader :group, :default_project_filter
def initialize(current_user, limit_project_ids, limit_projects, group, query, public_and_internal_projects, default_project_filter: false) def initialize(current_user, query, limit_projects = nil, group:, public_and_internal_projects: false, default_project_filter: false)
super(current_user, query, limit_project_ids, limit_projects, public_and_internal_projects)
@default_project_filter = default_project_filter
@group = group @group = group
@default_project_filter = default_project_filter
super(current_user, query, limit_projects, public_and_internal_projects: public_and_internal_projects)
end end
def generic_search_results def generic_search_results
@generic_search_results ||= Gitlab::GroupSearchResults.new(current_user, limit_projects, group, query, default_project_filter: default_project_filter) @generic_search_results ||= Gitlab::GroupSearchResults.new(
current_user,
query,
limit_projects,
group: group
)
end end
end end
end end
......
...@@ -11,16 +11,20 @@ module Gitlab ...@@ -11,16 +11,20 @@ module Gitlab
delegate :users, to: :generic_search_results delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, project, repository_ref = nil) def initialize(current_user, query, project:, repository_ref: nil)
@current_user = current_user
@project = project @project = project
@repository_ref = repository_ref.presence || project.default_branch @repository_ref = repository_ref.presence || project.default_branch
@query = query
@public_and_internal_projects = false super(current_user, query, [project], public_and_internal_projects: false)
end end
def generic_search_results def generic_search_results
@generic_search_results ||= Gitlab::ProjectSearchResults.new(current_user, project, query, repository_ref) @generic_search_results ||= Gitlab::ProjectSearchResults.new(
current_user,
query,
project: project,
repository_ref: repository_ref
)
end end
private private
...@@ -95,10 +99,6 @@ module Gitlab ...@@ -95,10 +99,6 @@ module Gitlab
end end
end end
def limit_project_ids
[project.id]
end
def root_ref? def root_ref?
project.root_ref?(repository_ref) project.root_ref?(repository_ref)
end end
......
...@@ -9,18 +9,17 @@ module Gitlab ...@@ -9,18 +9,17 @@ module Gitlab
attr_reader :current_user, :query, :public_and_internal_projects attr_reader :current_user, :query, :public_and_internal_projects
# Limit search results by passed project ids # Limit search results by passed projects
# It allows us to search only for projects user has access to # It allows us to search only for projects user has access to
attr_reader :limit_project_ids, :limit_projects attr_reader :limit_projects
delegate :users, to: :generic_search_results delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, limit_project_ids, limit_projects = nil, public_and_internal_projects = true) def initialize(current_user, query, limit_projects = nil, public_and_internal_projects: true)
@current_user = current_user @current_user = current_user
@limit_project_ids = limit_project_ids
@limit_projects = limit_projects
@query = query @query = query
@limit_projects = limit_projects
@public_and_internal_projects = public_and_internal_projects @public_and_internal_projects = public_and_internal_projects
end end
...@@ -52,7 +51,7 @@ module Gitlab ...@@ -52,7 +51,7 @@ module Gitlab
end end
def generic_search_results def generic_search_results
@generic_search_results ||= Gitlab::SearchResults.new(current_user, limit_projects, query) @generic_search_results ||= Gitlab::SearchResults.new(current_user, query, limit_projects)
end end
def formatted_count(scope) def formatted_count(scope)
...@@ -110,6 +109,17 @@ module Gitlab ...@@ -110,6 +109,17 @@ module Gitlab
@milestones_count ||= milestones.total_count @milestones_count ||= milestones.total_count
end end
# mbergeron: these aliases act as an adapter to the Gitlab::SearchResults
# interface, which is mostly implemented by this class.
alias_method :limited_projects_count, :projects_count
alias_method :limited_notes_count, :notes_count
alias_method :limited_blobs_count, :blobs_count
alias_method :limited_wiki_blobs_count, :wiki_blobs_count
alias_method :limited_commits_count, :commits_count
alias_method :limited_issues_count, :issues_count
alias_method :limited_merge_requests_count, :merge_requests_count
alias_method :limited_milestones_count, :milestones_count
def single_commit_result? def single_commit_result?
false false
end end
...@@ -165,6 +175,20 @@ module Gitlab ...@@ -165,6 +175,20 @@ module Gitlab
private private
# Convert the `limit_projects` to a list of ids for Elasticsearch
def limit_project_ids
strong_memoize(:limit_project_ids) do
case limit_projects
when :any then :any
when ActiveRecord::Relation
limit_projects.pluck_primary_key if limit_projects.model == Project
when Array
limit_projects.all? { |x| x.is_a?(Project) } ? limit_projects.map(&:id) : []
else []
end
end
end
# Apply some eager loading to the `records` of an ES result object without # Apply some eager loading to the `records` of an ES result object without
# losing pagination information. Also, take advantage of preload method if # losing pagination information. Also, take advantage of preload method if
# provided by the caller. # provided by the caller.
...@@ -216,11 +240,7 @@ module Gitlab ...@@ -216,11 +240,7 @@ module Gitlab
def merge_requests def merge_requests
strong_memoize(:merge_requests) do strong_memoize(:merge_requests) do
options = base_options.merge( MergeRequest.elastic_search(query, options: base_options)
project_ids: filter_project_ids_by_feature(:merge_requests, limit_project_ids)
)
MergeRequest.elastic_search(query, options: options)
end end
end end
...@@ -234,15 +254,11 @@ module Gitlab ...@@ -234,15 +254,11 @@ module Gitlab
return Kaminari.paginate_array([]) if query.blank? return Kaminari.paginate_array([]) if query.blank?
strong_memoize(:blobs) do strong_memoize(:blobs) do
options = base_options.merge(
additional_filter: repository_filter(limit_project_ids)
)
Repository.__elasticsearch__.elastic_search_as_found_blob( Repository.__elasticsearch__.elastic_search_as_found_blob(
query, query,
page: (page || 1).to_i, page: (page || 1).to_i,
per: per_page, per: per_page,
options: options options: base_options
) )
end end
end end
...@@ -251,15 +267,11 @@ module Gitlab ...@@ -251,15 +267,11 @@ module Gitlab
return Kaminari.paginate_array([]) if query.blank? return Kaminari.paginate_array([]) if query.blank?
strong_memoize(:wiki_blobs) do strong_memoize(:wiki_blobs) do
options = base_options.merge(
additional_filter: wiki_filter(limit_project_ids)
)
ProjectWiki.__elasticsearch__.elastic_search_as_wiki_page( ProjectWiki.__elasticsearch__.elastic_search_as_wiki_page(
query, query,
page: (page || 1).to_i, page: (page || 1).to_i,
per: per_page, per: per_page,
options: options options: base_options
) )
end end
end end
...@@ -274,104 +286,16 @@ module Gitlab ...@@ -274,104 +286,16 @@ module Gitlab
return Kaminari.paginate_array([]) if query.blank? return Kaminari.paginate_array([]) if query.blank?
strong_memoize(:commits) do strong_memoize(:commits) do
options = base_options.merge(
additional_filter: repository_filter(limit_project_ids)
)
Repository.find_commits_by_message_with_elastic( Repository.find_commits_by_message_with_elastic(
query, query,
page: (page || 1).to_i, page: (page || 1).to_i,
per_page: per_page, per_page: per_page,
options: options, options: base_options,
preload_method: preload_method preload_method: preload_method
) )
end end
end end
def wiki_filter(project_ids)
blob_filter(:wiki, project_ids)
end
def repository_filter(project_ids)
blob_filter(:repository, project_ids)
end
def filter_project_ids_by_feature(feature, project_ids)
return project_ids if project_ids == :any
Project
.id_in(project_ids)
.filter_by_feature_visibility(feature, current_user)
.pluck_primary_key
end
def blob_filter(feature, project_ids)
key_name = "#{feature}_access_level"
project_ids = filter_project_ids_by_feature(feature, project_ids)
conditions =
if project_ids == :any
[{ exists: { field: "id" } }]
else
[{ terms: { id: project_ids } }]
end
if public_and_internal_projects
conditions << {
bool: {
filter: [
{ term: { visibility_level: Project::PUBLIC } },
{ term: { key_name => ProjectFeature::ENABLED } }
]
}
}
if current_user && !current_user.external?
conditions << {
bool: {
filter: [
{ term: { visibility_level: Project::INTERNAL } },
{ term: { key_name => ProjectFeature::ENABLED } }
]
}
}
end
end
{
has_parent: {
parent_type: 'project',
query: {
bool: {
should: conditions,
must_not: { term: { key_name => ProjectFeature::DISABLED } }
}
}
}
}
end
# rubocop: disable CodeReuse/ActiveRecord
def guest_project_ids
if current_user
current_user.authorized_projects
.where('project_authorizations.access_level = ?', Gitlab::Access::GUEST)
.pluck(:id)
else
[]
end
end
# rubocop: enable CodeReuse/ActiveRecord
def non_guest_project_ids
if limit_project_ids == :any
:any
else
@non_guest_project_ids ||= limit_project_ids - guest_project_ids
end
end
def default_scope def default_scope
'projects' 'projects'
end end
......
...@@ -21,7 +21,7 @@ RSpec.describe 'GlobalSearch', :elastic do ...@@ -21,7 +21,7 @@ RSpec.describe 'GlobalSearch', :elastic do
project.add_guest(guest) project.add_guest(guest)
end end
context "Respect feature visibility levels" do context "Respect feature visibility levels", :aggregate_failures do
context "Private projects" do context "Private projects" do
let(:project) { create(:project, :private, :repository, :wiki_repo) } let(:project) { create(:project, :private, :repository, :wiki_repo) }
...@@ -127,7 +127,7 @@ RSpec.describe 'GlobalSearch', :elastic do ...@@ -127,7 +127,7 @@ RSpec.describe 'GlobalSearch', :elastic do
expect_items_to_be_found(nil) expect_items_to_be_found(nil)
end end
it "shows items to member only if features are private" do it "shows items to member only if features are private", :aggregate_failures do
create_items(project, feature_settings(:private)) create_items(project, feature_settings(:private))
expect_items_to_be_found(admin) expect_items_to_be_found(admin)
...@@ -178,9 +178,9 @@ RSpec.describe 'GlobalSearch', :elastic do ...@@ -178,9 +178,9 @@ RSpec.describe 'GlobalSearch', :elastic do
check_count = lambda do |feature, c| check_count = lambda do |feature, c|
if arr.include?(feature) if arr.include?(feature)
expect(c).to be > 0 expect(c).to be > 0, "Search returned no #{feature} for #{user}"
else else
expect(c).to eq(0) expect(c).to eq(0), "Search returned #{feature} for #{user}"
end end
end end
......
...@@ -7,7 +7,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -7,7 +7,7 @@ RSpec.describe Gitlab::SearchResults do
let_it_be(:compliance_project) { create(:project, :with_compliance_framework, name: 'foo') } let_it_be(:compliance_project) { create(:project, :with_compliance_framework, name: 'foo') }
subject { described_class.new(user, Project.all, 'foo') } subject { described_class.new(user, 'foo') }
describe '#projects' do describe '#projects' do
it 'avoid N+1 queries' do it 'avoid N+1 queries' do
......
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Elastic::GroupSearchResults do RSpec.describe Gitlab::Elastic::GroupSearchResults do
subject(:results) { described_class.new(user, nil, nil, group, query, nil) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:guest) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::GUEST) } } let_it_be(:guest) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::GUEST) } }
subject(:results) { described_class.new(user, query, group: group) }
before do before do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
end end
...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults do ...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults do
expect(Gitlab::GroupSearchResults).to receive(:new).and_call_original expect(Gitlab::GroupSearchResults).to receive(:new).and_call_original
end end
it { expect(results.objects('users')).to eq([guest]) } it { expect(results.objects('users')).to contain_exactly(guest) }
it { expect(results.limited_users_count).to eq(1) } it { expect(results.limited_users_count).to eq(1) }
describe 'pagination' do describe 'pagination' do
...@@ -29,8 +29,8 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults do ...@@ -29,8 +29,8 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults do
let_it_be(:user2) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::REPORTER) } } let_it_be(:user2) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::REPORTER) } }
it 'returns the correct page of results' do it 'returns the correct page of results' do
expect(results.objects('users', page: 1, per_page: 1)).to eq([user2]) expect(results.objects('users', page: 1, per_page: 1)).to contain_exactly(user2)
expect(results.objects('users', page: 2, per_page: 1)).to eq([guest]) expect(results.objects('users', page: 2, per_page: 1)).to contain_exactly(guest)
end end
it 'returns the correct number of results for one page' do it 'returns the correct number of results for one page' do
......
...@@ -6,13 +6,16 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -6,13 +6,16 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:query) { 'hello world' } let(:query) { 'hello world' }
let(:repository_ref) { nil }
subject(:results) { described_class.new(user, query, project: project, repository_ref: repository_ref) }
before do before do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
end end
describe 'initialize with empty ref' do describe 'initialize with empty ref' do
subject(:results) { described_class.new(user, query, project, '') } let(:repository_ref) { '' }
it { expect(results.project).to eq(project) } it { expect(results.project).to eq(project) }
it { expect(results.repository_ref).to eq('master') } it { expect(results.repository_ref).to eq('master') }
...@@ -20,58 +23,48 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -20,58 +23,48 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
end end
describe 'initialize with ref' do describe 'initialize with ref' do
let(:ref) { 'refs/heads/test' } let(:repository_ref) { 'refs/heads/test' }
subject(:results) { described_class.new(user, query, project, ref) }
it { expect(results.project).to eq(project) } it { expect(results.project).to eq(project) }
it { expect(results.repository_ref).to eq(ref) } it { expect(results.repository_ref).to eq(repository_ref) }
it { expect(results.query).to eq('hello world') } it { expect(results.query).to eq('hello world') }
end end
describe "search", :sidekiq_might_not_need_inline do describe "search", :sidekiq_might_not_need_inline do
it "returns correct amounts" do let(:project) { create(:project, :public, :repository, :wiki_repo) }
project = create :project, :public, :repository, :wiki_repo let(:private_project) { create(:project, :repository, :wiki_repo) }
project1 = create :project, :public, :repository, :wiki_repo
project.repository.index_commits_and_blobs
# Notes
create :note, note: 'bla-bla term', project: project
# The note in the project you have no access to
create :note, note: 'bla-bla term', project: project1
# Wiki before do
project.wiki.create_page('index_page', 'term') [project, private_project].each do |project|
project.wiki.index_wiki_blobs create(:note, note: 'bla-bla term', project: project)
project1.wiki.create_page('index_page', ' term') project.wiki.create_page('index_page', 'term')
project1.wiki.index_wiki_blobs project.wiki.index_wiki_blobs
end
project.repository.index_commits_and_blobs
ensure_elasticsearch_index! ensure_elasticsearch_index!
end
result = described_class.new(user, 'term', project) it "returns correct amounts" do
result = described_class.new(user, 'term', project: project)
expect(result.notes_count).to eq(1) expect(result.notes_count).to eq(1)
expect(result.wiki_blobs_count).to eq(1) expect(result.wiki_blobs_count).to eq(1)
expect(result.blobs_count).to eq(1) expect(result.blobs_count).to eq(1)
result1 = described_class.new(user, 'initial', project) result = described_class.new(user, 'initial', project: project)
expect(result1.commits_count).to eq(1) expect(result.commits_count).to eq(1)
end end
context 'visibility checks' do context 'visibility checks' do
it 'shows wiki for guests' do let(:project) { create(:project, :public, :wiki_repo) }
project = create :project, :public, :wiki_repo let(:query) { 'term' }
guest = create :user
project.add_guest(guest)
# Wiki
project.wiki.create_page('index_page', 'term')
project.wiki.index_wiki_blobs
ensure_elasticsearch_index! before do
project.add_guest(user)
end
result = described_class.new(guest, 'term', project) it 'shows wiki for guests' do
expect(result.wiki_blobs_count).to eq(1) expect(results.wiki_blobs_count).to eq(1)
end end
end end
end end
...@@ -79,12 +72,13 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -79,12 +72,13 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
describe "search for commits in non-default branch" do describe "search for commits in non-default branch" do
let(:project) { create(:project, :public, :repository, visibility) } let(:project) { create(:project, :public, :repository, visibility) }
let(:visibility) { :repository_enabled } let(:visibility) { :repository_enabled }
let(:result) { described_class.new(user, 'initial', project, 'test') } let(:query) { 'initial' }
let(:repository_ref) { 'test' }
subject(:commits) { result.objects('commits') } subject(:commits) { results.objects('commits') }
it 'finds needed commit' do it 'finds needed commit' do
expect(result.commits_count).to eq(1) expect(results.commits_count).to eq(1)
end end
it 'responds to total_pages method' do it 'responds to total_pages method' do
...@@ -122,9 +116,10 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -122,9 +116,10 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
describe 'search for blobs in non-default branch' do describe 'search for blobs in non-default branch' do
let(:project) { create(:project, :public, :repository, :repository_private) } let(:project) { create(:project, :public, :repository, :repository_private) }
let(:result) { described_class.new(user, 'initial', project, 'test') } let(:query) { 'initial' }
let(:repository_ref) { 'test' }
subject(:blobs) { result.objects('blobs') } subject(:blobs) { results.objects('blobs') }
it 'always returns zero results' do it 'always returns zero results' do
expect_any_instance_of(Gitlab::FileFinder).to receive(:find).never expect_any_instance_of(Gitlab::FileFinder).to receive(:find).never
...@@ -134,88 +129,14 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -134,88 +129,14 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
end end
describe 'confidential issues', :sidekiq_might_not_need_inline do describe 'confidential issues', :sidekiq_might_not_need_inline do
let(:query) { 'issue' } include_examples 'access restricted confidential issues' do
let(:author) { create(:user) } before do
let(:assignee) { create(:user) } ensure_elasticsearch_index!
let(:non_member) { create(:user) } end
let(:member) { create(:user) }
let(:admin) { create(:admin) }
let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignees: [assignee]) }
before do
ensure_elasticsearch_index!
end
it 'does not list project confidential issues for non project members' do
results = described_class.new(non_member, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.issues_count).to eq 1
end
it 'lists project confidential issues for author' do
results = described_class.new(author, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.issues_count).to eq 2
end
it 'lists project confidential issues for assignee' do
results = described_class.new(assignee, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).to include security_issue_2
expect(results.issues_count).to eq 2
end
it 'lists project confidential issues for project members' do
project.add_developer(member)
results = described_class.new(member, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).to include security_issue_2
expect(results.issues_count).to eq 3
end
it 'does not list project confidential issues for project members with guest role' do
project.add_guest(member)
results = described_class.new(member, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.issues_count).to eq 1
end
it 'lists all project issues for admin' do
results = described_class.new(admin, query, project)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).to include security_issue_2
expect(results.issues_count).to eq 3
end end
end end
context 'user search' do context 'user search' do
subject(:results) { described_class.new(user, query, project) }
let(:query) { project.owner.username } let(:query) { project.owner.username }
before do before do
...@@ -243,6 +164,7 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -243,6 +164,7 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
context 'query performance' do context 'query performance' do
let(:project) { create(:project, :public, :repository, :wiki_repo) } let(:project) { create(:project, :public, :repository, :wiki_repo) }
let(:query) { '*' }
before do before do
# wiki_blobs method checks to see if there is a wiki page before doing # wiki_blobs method checks to see if there is a wiki page before doing
...@@ -250,8 +172,6 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -250,8 +172,6 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
create(:wiki_page, wiki: project.wiki) create(:wiki_page, wiki: project.wiki)
end end
let(:results) { described_class.new(user, '*', project) }
include_examples 'does not hit Elasticsearch twice for objects and counts', %w|notes blobs wiki_blobs commits issues merge_requests milestones| include_examples 'does not hit Elasticsearch twice for objects and counts', %w|notes blobs wiki_blobs commits issues merge_requests milestones|
end end
end end
...@@ -10,12 +10,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -10,12 +10,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project_1) { create(:project, :public, :repository, :wiki_repo) } let(:project_1) { create(:project, :public, :repository, :wiki_repo) }
let(:project_2) { create(:project, :public, :repository, :wiki_repo) } let(:project_2) { create(:project, :public, :repository, :wiki_repo) }
let(:limit_project_ids) { [project_1.id] } let(:limit_projects) { [project_1] }
describe '#formatted_count' do describe '#formatted_count' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let(:results) { described_class.new(user, 'hello world', limit_project_ids) } let(:results) { described_class.new(user, 'hello world', limit_projects) }
where(:scope, :count_method, :expected) do where(:scope, :count_method, :expected) do
'projects' | :projects_count | '1234' 'projects' | :projects_count | '1234'
...@@ -43,7 +43,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -43,7 +43,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
shared_examples_for 'a paginated object' do |object_type| shared_examples_for 'a paginated object' do |object_type|
let(:results) { described_class.new(user, 'hello world', limit_project_ids) } let(:results) { described_class.new(user, 'hello world', limit_projects) }
it 'does not explode when given a page as a string' do it 'does not explode when given a page as a string' do
expect { results.objects(object_type, page: "2") }.not_to raise_error expect { results.objects(object_type, page: "2") }.not_to raise_error
...@@ -135,7 +135,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -135,7 +135,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'issues' it_behaves_like 'a paginated object', 'issues'
it 'lists found issues' do it 'lists found issues' do
results = described_class.new(user, 'hello world', limit_project_ids) results = described_class.new(user, 'hello world', limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue_1 expect(issues).to include @issue_1
...@@ -145,14 +145,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -145,14 +145,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'returns empty list when issues are not found' do it 'returns empty list when issues are not found' do
results = described_class.new(user, 'security', limit_project_ids) results = described_class.new(user, 'security', limit_projects)
expect(results.objects('issues')).to be_empty expect(results.objects('issues')).to be_empty
expect(results.issues_count).to eq 0 expect(results.issues_count).to eq 0
end end
it 'lists issue when search by a valid iid' do it 'lists issue when search by a valid iid' do
results = described_class.new(user, '#2', limit_project_ids, nil, false) results = described_class.new(user, '#2', limit_projects, public_and_internal_projects: false)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).not_to include @issue_1 expect(issues).not_to include @issue_1
...@@ -162,7 +162,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -162,7 +162,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'returns empty list when search by invalid iid' do it 'returns empty list when search by invalid iid' do
results = described_class.new(user, '#222', limit_project_ids) results = described_class.new(user, '#222', limit_projects)
expect(results.objects('issues')).to be_empty expect(results.objects('issues')).to be_empty
expect(results.issues_count).to eq 0 expect(results.issues_count).to eq 0
...@@ -198,7 +198,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -198,7 +198,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'notes' it_behaves_like 'a paginated object', 'notes'
it 'lists found notes' do it 'lists found notes' do
results = described_class.new(user, 'foo', limit_project_ids) results = described_class.new(user, 'foo', limit_projects)
notes = results.objects('notes') notes = results.objects('notes')
expect(notes).to include @note_1 expect(notes).to include @note_1
...@@ -208,7 +208,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -208,7 +208,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'returns empty list when notes are not found' do it 'returns empty list when notes are not found' do
results = described_class.new(user, 'security', limit_project_ids) results = described_class.new(user, 'security', limit_projects)
expect(results.objects('notes')).to be_empty expect(results.objects('notes')).to be_empty
expect(results.notes_count).to eq 0 expect(results.notes_count).to eq 0
...@@ -218,7 +218,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -218,7 +218,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
describe 'confidential issues' do describe 'confidential issues' do
let(:project_3) { create(:project, :public) } let(:project_3) { create(:project, :public) }
let(:project_4) { create(:project, :public) } let(:project_4) { create(:project, :public) }
let(:limit_project_ids) { [project_1.id, project_2.id, project_3.id] } let(:limit_projects) { [project_1, project_2, project_3] }
let(:author) { create(:user) } let(:author) { create(:user) }
let(:assignee) { create(:user) } let(:assignee) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
...@@ -240,7 +240,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -240,7 +240,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:query) { 'issue' } let(:query) { 'issue' }
it 'does not list confidential issues for guests' do it 'does not list confidential issues for guests' do
results = described_class.new(nil, query, limit_project_ids) results = described_class.new(nil, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -253,7 +253,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -253,7 +253,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'does not list confidential issues for non project members' do it 'does not list confidential issues for non project members' do
results = described_class.new(non_member, query, limit_project_ids) results = described_class.new(non_member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -266,7 +266,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -266,7 +266,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists confidential issues for author' do it 'lists confidential issues for author' do
results = described_class.new(author, query, limit_project_ids) results = described_class.new(author, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -279,7 +279,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -279,7 +279,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists confidential issues for assignee' do it 'lists confidential issues for assignee' do
results = described_class.new(assignee, query, limit_project_ids) results = described_class.new(assignee, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -295,7 +295,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -295,7 +295,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_1.add_developer(member) project_1.add_developer(member)
project_2.add_developer(member) project_2.add_developer(member)
results = described_class.new(member, query, limit_project_ids) results = described_class.new(member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -308,7 +308,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -308,7 +308,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists all issues for admin' do it 'lists all issues for admin' do
results = described_class.new(admin, query, limit_project_ids) results = described_class.new(admin, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -325,7 +325,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -325,7 +325,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:query) { '#1' } let(:query) { '#1' }
it 'does not list confidential issues for guests' do it 'does not list confidential issues for guests' do
results = described_class.new(nil, query, limit_project_ids) results = described_class.new(nil, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -338,7 +338,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -338,7 +338,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'does not list confidential issues for non project members' do it 'does not list confidential issues for non project members' do
results = described_class.new(non_member, query, limit_project_ids) results = described_class.new(non_member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -351,7 +351,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -351,7 +351,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists confidential issues for author' do it 'lists confidential issues for author' do
results = described_class.new(author, query, limit_project_ids) results = described_class.new(author, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -364,7 +364,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -364,7 +364,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists confidential issues for assignee' do it 'lists confidential issues for assignee' do
results = described_class.new(assignee, query, limit_project_ids) results = described_class.new(assignee, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -380,7 +380,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -380,7 +380,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_developer(member) project_2.add_developer(member)
project_3.add_developer(member) project_3.add_developer(member)
results = described_class.new(member, query, limit_project_ids) results = described_class.new(member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -393,7 +393,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -393,7 +393,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'lists all issues for admin' do it 'lists all issues for admin' do
results = described_class.new(admin, query, limit_project_ids) results = described_class.new(admin, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include @issue expect(issues).to include @issue
...@@ -439,7 +439,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -439,7 +439,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'merge_requests' it_behaves_like 'a paginated object', 'merge_requests'
it 'lists found merge requests' do it 'lists found merge requests' do
results = described_class.new(user, 'hello world', limit_project_ids) results = described_class.new(user, 'hello world', limit_projects)
merge_requests = results.objects('merge_requests') merge_requests = results.objects('merge_requests')
expect(merge_requests).to include @merge_request_1 expect(merge_requests).to include @merge_request_1
...@@ -449,14 +449,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -449,14 +449,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'returns empty list when merge requests are not found' do it 'returns empty list when merge requests are not found' do
results = described_class.new(user, 'security', limit_project_ids) results = described_class.new(user, 'security', limit_projects)
expect(results.objects('merge_requests')).to be_empty expect(results.objects('merge_requests')).to be_empty
expect(results.merge_requests_count).to eq 0 expect(results.merge_requests_count).to eq 0
end end
it 'lists merge request when search by a valid iid' do it 'lists merge request when search by a valid iid' do
results = described_class.new(user, '#2', limit_project_ids) results = described_class.new(user, '#2', limit_projects)
merge_requests = results.objects('merge_requests') merge_requests = results.objects('merge_requests')
expect(merge_requests).not_to include @merge_request_1 expect(merge_requests).not_to include @merge_request_1
...@@ -466,7 +466,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -466,7 +466,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'returns empty list when search by invalid iid' do it 'returns empty list when search by invalid iid' do
results = described_class.new(user, '#222', limit_project_ids) results = described_class.new(user, '#222', limit_projects)
expect(results.objects('merge_requests')).to be_empty expect(results.objects('merge_requests')).to be_empty
expect(results.merge_requests_count).to eq 0 expect(results.merge_requests_count).to eq 0
...@@ -500,7 +500,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -500,7 +500,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
result = described_class.new(user, 'term', [project.id]) result = described_class.new(user, 'term', [project])
expect(result.issues_count).to eq(2) expect(result.issues_count).to eq(2)
expect(result.merge_requests_count).to eq(2) expect(result.merge_requests_count).to eq(2)
...@@ -517,13 +517,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -517,13 +517,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
def search_for(term) def search_for(term)
described_class.new(user, term, [project_1.id]).objects('blobs').map(&:path) described_class.new(user, term, [project_1]).objects('blobs').map(&:path)
end end
it_behaves_like 'a paginated object', 'blobs' it_behaves_like 'a paginated object', 'blobs'
it 'finds blobs' do it 'finds blobs' do
results = described_class.new(user, 'def', limit_project_ids) results = described_class.new(user, 'def', limit_projects)
blobs = results.objects('blobs') blobs = results.objects('blobs')
expect(blobs.first.data).to include('def') expect(blobs.first.data).to include('def')
...@@ -531,7 +531,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -531,7 +531,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
it 'finds blobs by prefix search' do it 'finds blobs by prefix search' do
results = described_class.new(user, 'defau*', limit_project_ids) results = described_class.new(user, 'defau*', limit_projects)
blobs = results.objects('blobs') blobs = results.objects('blobs')
expect(blobs.first.data).to include('default') expect(blobs.first.data).to include('default')
...@@ -544,18 +544,18 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -544,18 +544,18 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_reporter(user) project_2.add_reporter(user)
ensure_elasticsearch_index! ensure_elasticsearch_index!
results = described_class.new(user, 'def', [project_1.id]) results = described_class.new(user, 'def', [project_1])
expect(results.blobs_count).to eq 5 expect(results.blobs_count).to eq 5
result_project_ids = results.objects('blobs').map(&:project_id) result_project_ids = results.objects('blobs').map(&:project_id)
expect(result_project_ids.uniq).to eq([project_1.id]) expect(result_project_ids.uniq).to eq([project_1.id])
results = described_class.new(user, 'def', [project_1.id, project_2.id]) results = described_class.new(user, 'def', [project_1, project_2])
expect(results.blobs_count).to eq 10 expect(results.blobs_count).to eq 10
end end
it 'returns zero when blobs are not found' do it 'returns zero when blobs are not found' do
results = described_class.new(user, 'asdfg', limit_project_ids) results = described_class.new(user, 'asdfg', limit_projects)
expect(results.blobs_count).to eq 0 expect(results.blobs_count).to eq 0
end end
...@@ -720,7 +720,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -720,7 +720,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
describe 'Wikis' do describe 'Wikis' do
let(:results) { described_class.new(user, 'term', limit_project_ids) } let(:results) { described_class.new(user, 'term', limit_projects) }
subject(:wiki_blobs) { results.objects('wiki_blobs') } subject(:wiki_blobs) { results.objects('wiki_blobs') }
...@@ -759,12 +759,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -759,12 +759,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
expect(results.wiki_blobs_count).to eq 1 expect(results.wiki_blobs_count).to eq 1
results = described_class.new(user, 'term', [project_1.id, project_2.id]) results = described_class.new(user, 'term', [project_1, project_2])
expect(results.wiki_blobs_count).to eq 2 expect(results.wiki_blobs_count).to eq 2
end end
it 'returns zero when wiki blobs are not found' do it 'returns zero when wiki blobs are not found' do
results = described_class.new(user, 'asdfg', limit_project_ids) results = described_class.new(user, 'asdfg', limit_projects)
expect(results.wiki_blobs_count).to eq 0 expect(results.wiki_blobs_count).to eq 0
end end
...@@ -773,13 +773,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -773,13 +773,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:project_1) { create(:project, :public, :repository, :wiki_disabled) } let(:project_1) { create(:project, :public, :repository, :wiki_disabled) }
context 'search by member' do context 'search by member' do
let(:limit_project_ids) { [project_1.id] } let(:limit_projects) { [project_1] }
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
context 'search by non-member' do context 'search by non-member' do
let(:limit_project_ids) { [] } let(:limit_projects) { [] }
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
...@@ -789,7 +789,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -789,7 +789,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:project_1) { create(:project, :public, :repository, :wiki_private, :wiki_repo) } let(:project_1) { create(:project, :public, :repository, :wiki_private, :wiki_repo) }
context 'search by member' do context 'search by member' do
let(:limit_project_ids) { [project_1.id] } let(:limit_projects) { [project_1] }
before do before do
project_1.add_guest(user) project_1.add_guest(user)
...@@ -799,7 +799,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -799,7 +799,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
context 'search by non-member' do context 'search by non-member' do
let(:limit_project_ids) { [] } let(:limit_projects) { [] }
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
...@@ -815,7 +815,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -815,7 +815,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'commits' it_behaves_like 'a paginated object', 'commits'
it 'finds commits' do it 'finds commits' do
results = described_class.new(user, 'add', limit_project_ids) results = described_class.new(user, 'add', limit_projects)
commits = results.objects('commits') commits = results.objects('commits')
expect(commits.first.message.downcase).to include("add") expect(commits.first.message.downcase).to include("add")
...@@ -828,15 +828,15 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -828,15 +828,15 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_reporter(user) project_2.add_reporter(user)
ensure_elasticsearch_index! ensure_elasticsearch_index!
results = described_class.new(user, 'add', [project_1.id]) results = described_class.new(user, 'add', [project_1])
expect(results.commits_count).to eq 24 expect(results.commits_count).to eq 24
results = described_class.new(user, 'add', [project_1.id, project_2.id]) results = described_class.new(user, 'add', [project_1, project_2])
expect(results.commits_count).to eq 48 expect(results.commits_count).to eq 48
end end
it 'returns zero when commits are not found' do it 'returns zero when commits are not found' do
results = described_class.new(user, 'asdfg', limit_project_ids) results = described_class.new(user, 'asdfg', limit_projects)
expect(results.commits_count).to eq 0 expect(results.commits_count).to eq 0
end end
...@@ -847,7 +847,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -847,7 +847,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:private_project1) { create(:project, :private, :repository, :wiki_repo, description: "Private project") } let(:private_project1) { create(:project, :private, :repository, :wiki_repo, description: "Private project") }
let(:private_project2) { create(:project, :private, :repository, :wiki_repo, description: "Private project where I'm a member") } let(:private_project2) { create(:project, :private, :repository, :wiki_repo, description: "Private project where I'm a member") }
let(:public_project) { create(:project, :public, :repository, :wiki_repo, description: "Public project") } let(:public_project) { create(:project, :public, :repository, :wiki_repo, description: "Public project") }
let(:limit_project_ids) { [private_project2.id] } let(:limit_projects) { [private_project2] }
before do before do
private_project2.project_members.create(user: user, access_level: ProjectMember::DEVELOPER) private_project2.project_members.create(user: user, access_level: ProjectMember::DEVELOPER)
...@@ -863,7 +863,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -863,7 +863,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
# Authenticated search # Authenticated search
results = described_class.new(user, 'project', limit_project_ids) results = described_class.new(user, 'project', limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue_1 expect(issues).to include issue_1
...@@ -903,8 +903,8 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -903,8 +903,8 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
internal_project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED) internal_project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED)
ensure_elasticsearch_index! ensure_elasticsearch_index!
project_ids = user.authorized_projects.pluck(:id) projects = user.authorized_projects
results = described_class.new(user, 'project', project_ids) results = described_class.new(user, 'project', projects)
milestones = results.objects('milestones') milestones = results.objects('milestones')
expect(milestones).to match_array([milestone_1, milestone_3]) expect(milestones).to match_array([milestone_1, milestone_3])
...@@ -930,8 +930,8 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -930,8 +930,8 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
context 'when user can read milestones' do context 'when user can read milestones' do
it 'returns right set of milestones' do it 'returns right set of milestones' do
# Authenticated search # Authenticated search
project_ids = user.authorized_projects.pluck(:id) projects = user.authorized_projects
results = described_class.new(user, 'project', project_ids) results = described_class.new(user, 'project', projects)
milestones = results.objects('milestones') milestones = results.objects('milestones')
expect(milestones).to match_array([milestone_1, milestone_3, milestone_4]) expect(milestones).to match_array([milestone_1, milestone_3, milestone_4])
...@@ -1012,7 +1012,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1012,7 +1012,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
# Authenticated search # Authenticated search
results = described_class.new(user, 'project', limit_project_ids) results = described_class.new(user, 'project', limit_projects)
milestones = results.objects('projects') milestones = results.objects('projects')
expect(milestones).to include internal_project expect(milestones).to include internal_project
...@@ -1039,7 +1039,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1039,7 +1039,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
# Authenticated search # Authenticated search
results = described_class.new(user, 'project', limit_project_ids) results = described_class.new(user, 'project', limit_projects)
merge_requests = results.objects('merge_requests') merge_requests = results.objects('merge_requests')
expect(merge_requests).to include merge_request_1 expect(merge_requests).to include merge_request_1
...@@ -1068,7 +1068,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1068,7 +1068,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it 'finds the right set of wiki blobs' do it 'finds the right set of wiki blobs' do
# Authenticated search # Authenticated search
results = described_class.new(user, 'term', limit_project_ids) results = described_class.new(user, 'term', limit_projects)
blobs = results.objects('wiki_blobs') blobs = results.objects('wiki_blobs')
expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project] expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project]
...@@ -1100,7 +1100,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1100,7 +1100,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
# Authenticated search # Authenticated search
results = described_class.new(user, 'search', limit_project_ids) results = described_class.new(user, 'search', limit_projects)
commits = results.objects('commits') commits = results.objects('commits')
expect(commits.map(&:project)).to match_array [internal_project, private_project2, public_project] expect(commits.map(&:project)).to match_array [internal_project, private_project2, public_project]
...@@ -1132,7 +1132,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1132,7 +1132,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index! ensure_elasticsearch_index!
# Authenticated search # Authenticated search
results = described_class.new(user, 'tesla', limit_project_ids) results = described_class.new(user, 'tesla', limit_projects)
blobs = results.objects('blobs') blobs = results.objects('blobs')
expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project] expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project]
...@@ -1149,7 +1149,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -1149,7 +1149,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end end
context 'query performance' do context 'query performance' do
let(:results) { described_class.new(user, 'hello world', limit_project_ids) } let(:results) { described_class.new(user, 'hello world', limit_projects) }
include_examples 'does not hit Elasticsearch twice for objects and counts', %w|projects notes blobs wiki_blobs commits issues merge_requests milestones| include_examples 'does not hit Elasticsearch twice for objects and counts', %w|projects notes blobs wiki_blobs commits issues merge_requests milestones|
end end
......
...@@ -17,15 +17,11 @@ module SearchResultHelpers ...@@ -17,15 +17,11 @@ module SearchResultHelpers
if expected_count if expected_count
actual_count = results.public_send("#{target}_count") actual_count = results.public_send("#{target}_count")
expect(actual_count).to eq(expected_count), "#{target} expected count to be #{expected_count} for #{user_name}, got #{actual_count}" expect(actual_count).to eq(expected_count), "#{target} expected count to be #{expected_count} for #{user_name}, got #{actual_count}: #{objects}"
end end
if expected_objects if expected_objects
if expected_objects.empty? expect(objects).to contain_exactly(*expected_objects)
expect(objects.empty?).to eq(true)
else
expect(objects).to contain_exactly(*expected_objects)
end
end end
end end
end end
......
...@@ -4,10 +4,10 @@ module Gitlab ...@@ -4,10 +4,10 @@ module Gitlab
class GroupSearchResults < SearchResults class GroupSearchResults < SearchResults
attr_reader :group attr_reader :group
def initialize(current_user, limit_projects, group, query, default_project_filter: false) def initialize(current_user, query, limit_projects = nil, group:, default_project_filter: false)
super(current_user, limit_projects, query, default_project_filter: default_project_filter)
@group = group @group = group
super(current_user, query, limit_projects, default_project_filter: default_project_filter)
end end
# rubocop:disable CodeReuse/ActiveRecord # rubocop:disable CodeReuse/ActiveRecord
......
...@@ -4,11 +4,11 @@ module Gitlab ...@@ -4,11 +4,11 @@ module Gitlab
class ProjectSearchResults < SearchResults class ProjectSearchResults < SearchResults
attr_reader :project, :repository_ref attr_reader :project, :repository_ref
def initialize(current_user, project, query, repository_ref = nil) def initialize(current_user, query, project:, repository_ref: nil)
@current_user = current_user
@project = project @project = project
@repository_ref = repository_ref.presence @repository_ref = repository_ref.presence
@query = query
super(current_user, query, [project])
end end
def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE, preload_method: nil) def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE, preload_method: nil)
......
...@@ -19,10 +19,10 @@ module Gitlab ...@@ -19,10 +19,10 @@ module Gitlab
# query # query
attr_reader :default_project_filter attr_reader :default_project_filter
def initialize(current_user, limit_projects, query, default_project_filter: false) def initialize(current_user, query, limit_projects = nil, default_project_filter: false)
@current_user = current_user @current_user = current_user
@limit_projects = limit_projects || Project.all
@query = query @query = query
@limit_projects = limit_projects || Project.all
@default_project_filter = default_project_filter @default_project_filter = default_project_filter
end end
......
...@@ -4,11 +4,8 @@ module Gitlab ...@@ -4,11 +4,8 @@ module Gitlab
class SnippetSearchResults < SearchResults class SnippetSearchResults < SearchResults
include SnippetsHelper include SnippetsHelper
attr_reader :current_user
def initialize(current_user, query) def initialize(current_user, query)
@current_user = current_user super(current_user, query)
@query = query
end end
def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE, preload_method: nil) def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE, preload_method: nil)
......
...@@ -4,9 +4,12 @@ require 'spec_helper' ...@@ -4,9 +4,12 @@ require 'spec_helper'
RSpec.describe Gitlab::GroupSearchResults do RSpec.describe Gitlab::GroupSearchResults do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) }
subject(:results) { described_class.new(user, 'gob', anything, group: group) }
describe 'user search' do describe 'user search' do
let(:group) { create(:group) } subject(:objects) { results.objects('users') }
it 'returns the users belonging to the group matching the search query' do it 'returns the users belonging to the group matching the search query' do
user1 = create(:user, username: 'gob_bluth') user1 = create(:user, username: 'gob_bluth')
...@@ -17,9 +20,7 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -17,9 +20,7 @@ RSpec.describe Gitlab::GroupSearchResults do
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users') is_expected.to eq [user1]
expect(result).to eq [user1]
end end
it 'returns the user belonging to the subgroup matching the search query' do it 'returns the user belonging to the subgroup matching the search query' do
...@@ -29,9 +30,7 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -29,9 +30,7 @@ RSpec.describe Gitlab::GroupSearchResults do
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users') is_expected.to eq [user1]
expect(result).to eq [user1]
end end
it 'returns the user belonging to the parent group matching the search query' do it 'returns the user belonging to the parent group matching the search query' do
...@@ -41,9 +40,7 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -41,9 +40,7 @@ RSpec.describe Gitlab::GroupSearchResults do
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users') is_expected.to eq [user1]
expect(result).to eq [user1]
end end
it 'does not return the user belonging to the private subgroup' do it 'does not return the user belonging to the private subgroup' do
...@@ -53,9 +50,7 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -53,9 +50,7 @@ RSpec.describe Gitlab::GroupSearchResults do
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users') is_expected.to be_empty
expect(result).to eq []
end end
it 'does not return the user belonging to an unrelated group' do it 'does not return the user belonging to an unrelated group' do
...@@ -63,15 +58,13 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -63,15 +58,13 @@ RSpec.describe Gitlab::GroupSearchResults do
unrelated_group = create(:group) unrelated_group = create(:group)
create(:group_member, :developer, user: user, group: unrelated_group) create(:group_member, :developer, user: user, group: unrelated_group)
result = described_class.new(user, anything, group, 'gob').objects('users') is_expected.to be_empty
expect(result).to eq []
end end
end
describe "#issuable_params" do
it 'sets include_subgroups flag by default' do it 'sets include_subgroups flag by default' do
result = described_class.new(user, anything, group, 'gob') expect(results.issuable_params[:include_subgroups]).to eq(true)
expect(result.issuable_params[:include_subgroups]).to eq(true)
end end
end end
end end
...@@ -8,28 +8,30 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -8,28 +8,30 @@ RSpec.describe Gitlab::ProjectSearchResults do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:query) { 'hello world' } let(:query) { 'hello world' }
let(:repository_ref) { nil }
describe 'initialize with empty ref' do subject(:results) { described_class.new(user, query, project: project, repository_ref: repository_ref) }
let(:results) { described_class.new(user, project, query, '') }
it { expect(results.project).to eq(project) } context 'with a repository_ref' do
it { expect(results.query).to eq('hello world') } context 'when empty' do
end let(:repository_ref) { '' }
it { expect(results.project).to eq(project) }
it { expect(results.query).to eq('hello world') }
end
describe 'initialize with ref' do context 'when set' do
let(:ref) { 'refs/heads/test' } let(:repository_ref) { 'refs/heads/test' }
let(:results) { described_class.new(user, project, query, ref) }
it { expect(results.project).to eq(project) } it { expect(results.project).to eq(project) }
it { expect(results.repository_ref).to eq(ref) } it { expect(results.repository_ref).to eq(repository_ref) }
it { expect(results.query).to eq('hello world') } it { expect(results.query).to eq('hello world') }
end
end end
describe '#formatted_count' do describe '#formatted_count' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let(:results) { described_class.new(user, project, query) }
where(:scope, :count_method, :expected) do where(:scope, :count_method, :expected) do
'blobs' | :limited_blobs_count | max_limited_count 'blobs' | :limited_blobs_count | max_limited_count
'notes' | :limited_notes_count | max_limited_count 'notes' | :limited_notes_count | max_limited_count
...@@ -63,7 +65,8 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -63,7 +65,8 @@ RSpec.describe Gitlab::ProjectSearchResults do
shared_examples 'general blob search' do |entity_type, blob_type| shared_examples 'general blob search' do |entity_type, blob_type|
let(:query) { 'files' } let(:query) { 'files' }
subject(:results) { described_class.new(user, project, query).objects(blob_type) }
subject(:objects) { results.objects(blob_type) }
context "when #{entity_type} is disabled" do context "when #{entity_type} is disabled" do
let(:project) { disabled_project } let(:project) { disabled_project }
...@@ -94,17 +97,17 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -94,17 +97,17 @@ RSpec.describe Gitlab::ProjectSearchResults do
end end
it 'finds by name' do it 'finds by name' do
expect(results.map(&:path)).to include(expected_file_by_path) expect(objects.map(&:path)).to include(expected_file_by_path)
end end
it "loads all blobs for path matches in single batch" do it "loads all blobs for path matches in single batch" do
expect(Gitlab::Git::Blob).to receive(:batch).once.and_call_original expect(Gitlab::Git::Blob).to receive(:batch).once.and_call_original
results.map(&:data) expect { objects.map(&:data) }.not_to raise_error
end end
it 'finds by content' do it 'finds by content' do
blob = results.select { |result| result.path == expected_file_by_content }.flatten.last blob = objects.select { |result| result.path == expected_file_by_content }.flatten.last
expect(blob.path).to eq(expected_file_by_content) expect(blob.path).to eq(expected_file_by_content)
end end
...@@ -115,7 +118,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -115,7 +118,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
let(:file_finder) { double } let(:file_finder) { double }
let(:project_branch) { 'project_branch' } let(:project_branch) { 'project_branch' }
subject(:results) { described_class.new(user, project, query, repository_ref).objects(blob_type) } subject(:objects) { results.objects(blob_type) }
before do before do
allow(entity).to receive(:default_branch).and_return(project_branch) allow(entity).to receive(:default_branch).and_return(project_branch)
...@@ -128,7 +131,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -128,7 +131,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
it 'uses it' do it 'uses it' do
expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder) expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder)
results expect { objects }.not_to raise_error
end end
end end
...@@ -138,7 +141,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -138,7 +141,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
it "uses #{entity_type} repository default reference" do it "uses #{entity_type} repository default reference" do
expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder) expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
results expect { objects }.not_to raise_error
end end
end end
...@@ -148,7 +151,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -148,7 +151,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
it "uses #{entity_type} repository default reference" do it "uses #{entity_type} repository default reference" do
expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder) expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
results expect { objects }.not_to raise_error
end end
end end
end end
...@@ -157,7 +160,6 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -157,7 +160,6 @@ RSpec.describe Gitlab::ProjectSearchResults do
let(:per_page) { 20 } let(:per_page) { 20 }
let(:count_limit) { described_class::COUNT_LIMIT } let(:count_limit) { described_class::COUNT_LIMIT }
let(:file_finder) { instance_double('Gitlab::FileFinder') } let(:file_finder) { instance_double('Gitlab::FileFinder') }
let(:results) { described_class.new(user, project, query) }
let(:repository_ref) { 'master' } let(:repository_ref) { 'master' }
before do before do
...@@ -228,139 +230,73 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -228,139 +230,73 @@ RSpec.describe Gitlab::ProjectSearchResults do
context 'return type' do context 'return type' do
let(:blobs) { [Gitlab::Search::FoundBlob.new(project: project)] } let(:blobs) { [Gitlab::Search::FoundBlob.new(project: project)] }
let(:results) { described_class.new(user, project, "Files", per_page: 20) } let(:query) { "Files" }
subject(:objects) { results.objects('wiki_blobs', per_page: 20) }
before do before do
allow(results).to receive(:wiki_blobs).and_return(blobs) allow(results).to receive(:wiki_blobs).and_return(blobs)
end end
it 'returns list of FoundWikiPage type object' do it 'returns list of FoundWikiPage type object' do
objects = results.objects('wiki_blobs')
expect(objects).to be_present expect(objects).to be_present
expect(objects).to all(be_a(Gitlab::Search::FoundWikiPage)) expect(objects).to all(be_a(Gitlab::Search::FoundWikiPage))
end end
end end
end end
it 'does not list issues on private projects' do describe 'issues search' do
issue = create(:issue, project: project) let(:issue) { create(:issue, project: project) }
let(:query) { issue.title }
results = described_class.new(user, project, issue.title)
expect(results.objects('issues')).not_to include issue
end
describe 'confidential issues' do
let(:query) { 'issue' }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, :internal) }
let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignees: [assignee]) }
it 'does not list project confidential issues for non project members' do
results = described_class.new(non_member, project, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.limited_issues_count).to eq 1
end
it 'does not list project confidential issues for project members with guest role' do
project.add_guest(member)
results = described_class.new(member, project, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).not_to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.limited_issues_count).to eq 1
end
it 'lists project confidential issues for author' do
results = described_class.new(author, project, query)
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).not_to include security_issue_2
expect(results.limited_issues_count).to eq 2
end
it 'lists project confidential issues for assignee' do
results = described_class.new(assignee, project, query)
issues = results.objects('issues')
expect(issues).to include issue subject(:objects) { results.objects('issues') }
expect(issues).not_to include security_issue_1
expect(issues).to include security_issue_2
expect(results.limited_issues_count).to eq 2
end
it 'lists project confidential issues for project members' do
project.add_developer(member)
results = described_class.new(member, project, query) it 'does not list issues on private projects' do
issues = results.objects('issues') expect(objects).not_to include issue
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).to include security_issue_2
expect(results.limited_issues_count).to eq 3
end end
it 'lists all project issues for admin' do describe "confidential issues" do
results = described_class.new(admin, project, query) include_examples "access restricted confidential issues"
issues = results.objects('issues')
expect(issues).to include issue
expect(issues).to include security_issue_1
expect(issues).to include security_issue_2
expect(results.limited_issues_count).to eq 3
end end
end end
describe 'notes search' do describe 'notes search' do
it 'lists notes' do let(:query) { note.note }
project = create(:project, :public)
note = create(:note, project: project)
results = described_class.new(user, project, note.note) subject(:notes) { results.objects('notes') }
expect(results.objects('notes')).to include note context 'with a public project' do
end let(:project) { create(:project, :public) }
let(:note) { create(:note, project: project) }
it "doesn't list issue notes when access is restricted" do it 'lists notes' do
project = create(:project, :public, :issues_private) expect(notes).to include note
note = create(:note_on_issue, project: project) end
end
results = described_class.new(user, project, note.note) context 'with private issues' do
let(:project) { create(:project, :public, :issues_private) }
let(:note) { create(:note_on_issue, project: project) }
expect(results.objects('notes')).not_to include note it "doesn't list issue notes when access is restricted" do
expect(notes).not_to include note
end
end end
it "doesn't list merge_request notes when access is restricted" do context 'with private merge requests' do
project = create(:project, :public, :merge_requests_private) let(:project) { create(:project, :public, :merge_requests_private) }
note = create(:note_on_merge_request, project: project) let(:note) { create(:note_on_merge_request, project: project) }
results = described_class.new(user, project, note.note) it "doesn't list merge_request notes when access is restricted" do
expect(notes).not_to include note
expect(results.objects('notes')).not_to include note end
end end
end end
describe '#limited_notes_count' do describe '#limited_notes_count' do
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let(:note) { create(:note_on_issue, project: project) } let(:note) { create(:note_on_issue, project: project) }
let(:results) { described_class.new(user, project, note.note) } let(:query) { note.note }
context 'when count_limit is lower than total amount' do context 'when count_limit is lower than total amount' do
before do before do
...@@ -375,11 +311,6 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -375,11 +311,6 @@ RSpec.describe Gitlab::ProjectSearchResults do
context 'when count_limit is higher than total amount' do context 'when count_limit is higher than total amount' do
it 'calls note finder multiple times to get the limited amount of notes' do it 'calls note finder multiple times to get the limited amount of notes' do
project = create(:project, :public)
note = create(:note_on_issue, project: project)
results = described_class.new(user, project, note.note)
expect(results).to receive(:notes_finder).exactly(4).times.and_call_original expect(results).to receive(:notes_finder).exactly(4).times.and_call_original
expect(results.limited_notes_count).to eq(1) expect(results.limited_notes_count).to eq(1)
end end
...@@ -395,7 +326,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -395,7 +326,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
.with(anything, anything, anything, described_class::COUNT_LIMIT) .with(anything, anything, anything, described_class::COUNT_LIMIT)
.and_call_original .and_call_original
described_class.new(user, project, '.').commits_count results.commits_count
end end
end end
...@@ -406,19 +337,23 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -406,19 +337,23 @@ RSpec.describe Gitlab::ProjectSearchResults do
# * commit # * commit
# #
shared_examples 'access restricted commits' do shared_examples 'access restricted commits' do
let(:query) { search_phrase }
context 'when project is internal' do context 'when project is internal' do
let(:project) { create(:project, :internal, :repository) } let(:project) { create(:project, :internal, :repository) }
it 'does not search if user is not authenticated' do subject(:commits) { results.objects('commits') }
commits = described_class.new(nil, project, search_phrase).objects('commits')
expect(commits).to be_empty it 'searches if user is authenticated' do
expect(commits).to contain_exactly commit
end end
it 'searches if user is authenticated' do context 'when the user is not authenticated' do
commits = described_class.new(user, project, search_phrase).objects('commits') let(:user) { nil }
expect(commits).to contain_exactly commit it 'does not search' do
expect(commits).to be_empty
end
end end
end end
...@@ -437,29 +372,35 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -437,29 +372,35 @@ RSpec.describe Gitlab::ProjectSearchResults do
user user
end end
it 'does not show commit to stranger' do let(:project) { private_project }
commits = described_class.new(nil, private_project, search_phrase).objects('commits')
subject(:commits) { results.objects('commits') }
expect(commits).to be_empty context 'when the user is not authenticated' do
let(:user) { nil }
it 'does not show commit to stranger' do
expect(commits).to be_empty
end
end end
context 'team access' do context 'team access' do
it 'shows commit to creator' do context 'when the user is the creator' do
commits = described_class.new(creator, private_project, search_phrase).objects('commits') let(:user) { creator }
expect(commits).to contain_exactly commit it { expect(commits).to contain_exactly commit }
end end
it 'shows commit to master' do context 'when the user is a master' do
commits = described_class.new(team_master, private_project, search_phrase).objects('commits') let(:user) { team_master }
expect(commits).to contain_exactly commit it { expect(commits).to contain_exactly commit }
end end
it 'shows commit to reporter' do context 'when the user is a reporter' do
commits = described_class.new(team_reporter, private_project, search_phrase).objects('commits') let(:user) { team_reporter }
expect(commits).to contain_exactly commit it { expect(commits).to contain_exactly commit }
end end
end end
end end
...@@ -471,9 +412,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -471,9 +412,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
it 'returns the correct results for each page' do it 'returns the correct results for each page' do
expect(results_page(1)).to contain_exactly(commit('b83d6e391c22777fca1ed3012fce84f633d7fed0')) expect(results_page(1)).to contain_exactly(commit('b83d6e391c22777fca1ed3012fce84f633d7fed0'))
expect(results_page(2)).to contain_exactly(commit('498214de67004b1da3d820901307bed2a68a8ef6')) expect(results_page(2)).to contain_exactly(commit('498214de67004b1da3d820901307bed2a68a8ef6'))
expect(results_page(3)).to contain_exactly(commit('1b12f15a11fc6e62177bef08f47bc7b5ce50b141')) expect(results_page(3)).to contain_exactly(commit('1b12f15a11fc6e62177bef08f47bc7b5ce50b141'))
end end
...@@ -506,7 +445,7 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -506,7 +445,7 @@ RSpec.describe Gitlab::ProjectSearchResults do
end end
def results_page(page) def results_page(page)
described_class.new(user, project, '.').objects('commits', per_page: 1, page: page) described_class.new(user, '.', project: project).objects('commits', per_page: 1, page: page)
end end
def commit(hash) def commit(hash)
...@@ -518,26 +457,27 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -518,26 +457,27 @@ RSpec.describe Gitlab::ProjectSearchResults do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:commit) { project.repository.commit('59e29889be61e6e0e5e223bfa9ac2721d31605b8') } let(:commit) { project.repository.commit('59e29889be61e6e0e5e223bfa9ac2721d31605b8') }
let(:message) { 'Sorry, I did a mistake' } let(:message) { 'Sorry, I did a mistake' }
let(:query) { message }
it 'finds commit by message' do subject(:commits) { results.objects('commits') }
commits = described_class.new(user, project, message).objects('commits')
it 'finds commit by message' do
expect(commits).to contain_exactly commit expect(commits).to contain_exactly commit
end end
it 'handles when no commit match' do context 'when there are not hits' do
commits = described_class.new(user, project, 'not really an existing description').objects('commits') let(:query) { 'not really an existing description' }
expect(commits).to be_empty it 'handles when no commit match' do
expect(commits).to be_empty
end
end end
context 'when repository_ref is provided' do context 'when repository_ref is provided' do
let(:message) { 'Feature added' } let(:query) { 'Feature added' }
let(:repository_ref) { 'feature' } let(:repository_ref) { 'feature' }
it 'searches in the specified ref' do it 'searches in the specified ref' do
commits = described_class.new(user, project, message, repository_ref).objects('commits')
# This commit is unique to the feature branch # This commit is unique to the feature branch
expect(commits).to contain_exactly(project.repository.commit('0b4bc9a49b562e85de7cc9e834518ea6828729b9')) expect(commits).to contain_exactly(project.repository.commit('0b4bc9a49b562e85de7cc9e834518ea6828729b9'))
end end
...@@ -557,14 +497,14 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -557,14 +497,14 @@ RSpec.describe Gitlab::ProjectSearchResults do
commit_hashes.each do |type, commit_hash| commit_hashes.each do |type, commit_hash|
it "shows commit by #{type} hash id" do it "shows commit by #{type} hash id" do
commits = described_class.new(user, project, commit_hash).objects('commits') commits = described_class.new(user, commit_hash, project: project).objects('commits')
expect(commits).to contain_exactly commit expect(commits).to contain_exactly commit
end end
end end
it 'handles not existing commit hash correctly' do it 'handles not existing commit hash correctly' do
commits = described_class.new(user, project, 'deadbeef').objects('commits') commits = described_class.new(user, 'deadbeef', project: project).objects('commits')
expect(commits).to be_empty expect(commits).to be_empty
end end
...@@ -577,9 +517,13 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -577,9 +517,13 @@ RSpec.describe Gitlab::ProjectSearchResults do
end end
describe 'user search' do describe 'user search' do
it 'returns the user belonging to the project matching the search query' do let(:query) { 'gob' }
project = create(:project) let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
subject(:objects) { results.objects('users') }
it 'returns the user belonging to the project matching the search query' do
user1 = create(:user, username: 'gob_bluth') user1 = create(:user, username: 'gob_bluth')
create(:project_member, :developer, user: user1, project: project) create(:project_member, :developer, user: user1, project: project)
...@@ -588,23 +532,16 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -588,23 +532,16 @@ RSpec.describe Gitlab::ProjectSearchResults do
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, project, 'gob').objects('users') expect(objects).to contain_exactly(user1)
expect(result).to eq [user1]
end end
it 'returns the user belonging to the group matching the search query' do it 'returns the user belonging to the group matching the search query' do
group = create(:group)
project = create(:project, namespace: group)
user1 = create(:user, username: 'gob_bluth') user1 = create(:user, username: 'gob_bluth')
create(:group_member, :developer, user: user1, group: group) create(:group_member, :developer, user: user1, group: group)
create(:user, username: 'gob_2018') create(:user, username: 'gob_2018')
result = described_class.new(user, project, 'gob').objects('users') expect(objects).to contain_exactly(user1)
expect(result).to eq [user1]
end end
end end
end end
...@@ -9,13 +9,10 @@ RSpec.describe Gitlab::SearchResults do ...@@ -9,13 +9,10 @@ RSpec.describe Gitlab::SearchResults do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, name: 'foo') } let!(:project) { create(:project, name: 'foo') }
let!(:issue) { create(:issue, project: project, title: 'foo') } let!(:issue) { create(:issue, project: project, title: 'foo') }
let!(:merge_request) { create(:merge_request, source_project: project, title: 'foo') }
let!(:merge_request) do
create(:merge_request, source_project: project, title: 'foo')
end
let!(:milestone) { create(:milestone, project: project, title: 'foo') } let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(user, Project.all, 'foo') }
subject(:results) { described_class.new(user, 'foo', Project.all) }
context 'as a user with access' do context 'as a user with access' do
before do before do
...@@ -133,7 +130,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -133,7 +130,7 @@ RSpec.describe Gitlab::SearchResults do
forked_project = fork_project(project, user) forked_project = fork_project(project, user)
merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo') merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo')
results = described_class.new(user, Project.where(id: forked_project.id), 'foo') results = described_class.new(user, 'foo', Project.where(id: forked_project.id))
expect(results.objects('merge_requests')).to include merge_request_2 expect(results.objects('merge_requests')).to include merge_request_2
end end
...@@ -214,7 +211,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -214,7 +211,7 @@ RSpec.describe Gitlab::SearchResults do
let!(:security_issue_5) { create(:issue, :confidential, project: project_4, title: 'Security issue 5') } let!(:security_issue_5) { create(:issue, :confidential, project: project_4, title: 'Security issue 5') }
it 'does not list confidential issues for non project members' do it 'does not list confidential issues for non project members' do
results = described_class.new(non_member, limit_projects, query) results = described_class.new(non_member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -230,7 +227,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -230,7 +227,7 @@ RSpec.describe Gitlab::SearchResults do
project_1.add_guest(member) project_1.add_guest(member)
project_2.add_guest(member) project_2.add_guest(member)
results = described_class.new(member, limit_projects, query) results = described_class.new(member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -243,7 +240,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -243,7 +240,7 @@ RSpec.describe Gitlab::SearchResults do
end end
it 'lists confidential issues for author' do it 'lists confidential issues for author' do
results = described_class.new(author, limit_projects, query) results = described_class.new(author, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -256,7 +253,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -256,7 +253,7 @@ RSpec.describe Gitlab::SearchResults do
end end
it 'lists confidential issues for assignee' do it 'lists confidential issues for assignee' do
results = described_class.new(assignee, limit_projects, query) results = described_class.new(assignee, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -272,7 +269,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -272,7 +269,7 @@ RSpec.describe Gitlab::SearchResults do
project_1.add_developer(member) project_1.add_developer(member)
project_2.add_developer(member) project_2.add_developer(member)
results = described_class.new(member, limit_projects, query) results = described_class.new(member, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -285,7 +282,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -285,7 +282,7 @@ RSpec.describe Gitlab::SearchResults do
end end
it 'lists all issues for admin' do it 'lists all issues for admin' do
results = described_class.new(admin, limit_projects, query) results = described_class.new(admin, query, limit_projects)
issues = results.objects('issues') issues = results.objects('issues')
expect(issues).to include issue expect(issues).to include issue
...@@ -323,7 +320,7 @@ RSpec.describe Gitlab::SearchResults do ...@@ -323,7 +320,7 @@ RSpec.describe Gitlab::SearchResults do
# Global search scope takes user authorized projects, internal projects and public projects. # Global search scope takes user authorized projects, internal projects and public projects.
limit_projects = ProjectsFinder.new(current_user: user).execute limit_projects = ProjectsFinder.new(current_user: user).execute
milestones = described_class.new(user, limit_projects, 'milestone').objects('milestones') milestones = described_class.new(user, 'milestone', limit_projects).objects('milestones')
expect(milestones).to match_array([milestone_1, milestone_2, milestone_3]) expect(milestones).to match_array([milestone_1, milestone_2, milestone_3])
end end
......
...@@ -261,6 +261,7 @@ RSpec.configure do |config| ...@@ -261,6 +261,7 @@ RSpec.configure do |config|
./spec/support/protected_tags ./spec/support/protected_tags
./spec/support/shared_examples/features ./spec/support/shared_examples/features
./spec/support/shared_examples/requests ./spec/support/shared_examples/requests
./spec/support/shared_examples/lib/gitlab
./spec/views ./spec/views
./spec/workers ./spec/workers
) )
......
# frozen_string_literal: true
RSpec.shared_examples 'access restricted confidential issues' do
let(:query) { 'issue' }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:project) { create(:project, :internal) }
let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignees: [assignee]) }
subject(:objects) do
described_class.new(user, query, project: project).objects('issues')
end
context 'when the user is non-member' do
let(:user) { create(:user) }
it 'does not list project confidential issues for non project members' do
expect(objects).to contain_exactly(issue)
expect(results.limited_issues_count).to eq 1
end
end
context 'when the member is guest' do
let(:user) do
create(:user) { |guest| project.add_guest(guest) }
end
it 'does not list project confidential issues for project members with guest role' do
expect(objects).to contain_exactly(issue)
expect(results.limited_issues_count).to eq 1
end
end
context 'when the user is the author' do
let(:user) { author }
it 'lists project confidential issues' do
expect(objects).to contain_exactly(issue,
security_issue_1)
expect(results.limited_issues_count).to eq 2
end
end
context 'when the user is the assignee' do
let(:user) { assignee }
it 'lists project confidential issues for assignee' do
expect(objects).to contain_exactly(issue,
security_issue_2)
expect(results.limited_issues_count).to eq 2
end
end
context 'when the user is a developper' do
let(:user) do
create(:user) { |user| project.add_developer(user) }
end
it 'lists project confidential issues' do
expect(objects).to contain_exactly(issue,
security_issue_1,
security_issue_2)
expect(results.limited_issues_count).to eq 3
end
end
context 'when the user is admin', :request_store do
let(:user) { create(:user, admin: true) }
it 'lists all project issues' do
expect(objects).to contain_exactly(issue,
security_issue_1,
security_issue_2)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment