Commit 6c25cadf authored by Oswaldo Ferreira's avatar Oswaldo Ferreira Committed by Douwe Maan

License plan check on namespaces

parent e72964c7
...@@ -174,7 +174,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -174,7 +174,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:shared_runners_minutes, :shared_runners_minutes,
:minimum_mirror_sync_time, :minimum_mirror_sync_time,
:geo_status_timeout, :geo_status_timeout,
:elasticsearch_experimental_indexer :elasticsearch_experimental_indexer,
:check_namespace_plan
] ]
end end
end end
...@@ -85,7 +85,8 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -85,7 +85,8 @@ class Admin::GroupsController < Admin::ApplicationController
def group_params_ee def group_params_ee
[ [
:repository_size_limit, :repository_size_limit,
:shared_runners_minutes_limit :shared_runners_minutes_limit,
:plan
] ]
end end
end end
...@@ -206,7 +206,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -206,7 +206,7 @@ class Admin::UsersController < Admin::ApplicationController
def user_params_ee def user_params_ee
[ [
:note, :note,
namespace_attributes: [:id, :shared_runners_minutes_limit] namespace_attributes: [:id, :shared_runners_minutes_limit, :plan]
] ]
end end
end end
...@@ -45,7 +45,7 @@ class Projects::PathLocksController < Projects::ApplicationController ...@@ -45,7 +45,7 @@ class Projects::PathLocksController < Projects::ApplicationController
private private
def check_license def check_license
unless license_allows_file_locks? unless @project.feature_available?(:file_lock)
flash[:alert] = 'You need a different license to enable FileLocks feature' flash[:alert] = 'You need a different license to enable FileLocks feature'
redirect_to admin_license_path redirect_to admin_license_path
end end
......
...@@ -52,7 +52,7 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -52,7 +52,7 @@ class Projects::RefsController < Projects::ApplicationController
contents.push(*tree.blobs) contents.push(*tree.blobs)
contents.push(*tree.submodules) contents.push(*tree.submodules)
show_path_locks = license_allows_file_locks? && @project.path_locks.any? show_path_locks = @project.feature_available?(:file_lock) && @project.path_locks.any?
@logs = contents[@offset, @limit].to_a.map do |content| @logs = contents[@offset, @limit].to_a.map do |content|
file = @path ? File.join(@path, content.name) : content.name file = @path ? File.join(@path, content.name) : content.name
......
module AuditorUserHelper module AuditorUserHelper
def license_allows_auditor_user? def license_allows_auditor_user?
@license_allows_auditor_user ||= (::License.current && ::License.current.add_on?('GitLab_Auditor_User')) @license_allows_auditor_user ||= (::License.current&.feature_available?(:auditor_user))
end end
end end
...@@ -3,10 +3,6 @@ module PathLocksHelper ...@@ -3,10 +3,6 @@ module PathLocksHelper
can?(current_user, :admin_path_locks, project) || path_lock.user == current_user can?(current_user, :admin_path_locks, project) || path_lock.user == current_user
end end
def license_allows_file_locks?
@license_allows_file_locks ||= (::License.current && ::License.current.add_on?('GitLab_FileLocks'))
end
def text_label_for_lock(file_lock, path) def text_label_for_lock(file_lock, path)
if file_lock.path == path if file_lock.path == path
"Locked by #{file_lock.user.name}" "Locked by #{file_lock.user.name}"
......
...@@ -109,7 +109,7 @@ module TreeHelper ...@@ -109,7 +109,7 @@ module TreeHelper
end end
def lock_file_link(project = @project, path = @path, html_options: {}) def lock_file_link(project = @project, path = @path, html_options: {})
return unless license_allows_file_locks? && current_user return unless project.feature_available?(:file_lock) && current_user
return if path.blank? return if path.blank?
path_lock = project.find_path_lock(path, downstream: true) path_lock = project.find_path_lock(path, downstream: true)
...@@ -169,7 +169,7 @@ module TreeHelper ...@@ -169,7 +169,7 @@ module TreeHelper
end end
def render_lock_icon(path) def render_lock_icon(path)
return unless license_allows_file_locks? return unless @project.feature_available?(:file_lock)
return unless @project.root_ref?(@ref) return unless @project.root_ref?(@ref)
if file_lock = @project.find_path_lock(path, exact_match: true) if file_lock = @project.find_path_lock(path, exact_match: true)
......
...@@ -10,5 +10,9 @@ module EE ...@@ -10,5 +10,9 @@ module EE
validates :shared_runners_minutes, validates :shared_runners_minutes,
numericality: { greater_than_or_equal_to: 0 } numericality: { greater_than_or_equal_to: 0 }
end end
def should_check_namespace_plan?
check_namespace_plan? && (::Gitlab.com? || Rails.env.development?)
end
end end
end end
...@@ -6,11 +6,36 @@ module EE ...@@ -6,11 +6,36 @@ module EE
module Namespace module Namespace
extend ActiveSupport::Concern extend ActiveSupport::Concern
BRONZE_PLAN = 'bronze'.freeze
SILVER_PLAN = 'silver'.freeze
GOLD_PLAN = 'gold'.freeze
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EE_PLANS = {
BRONZE_PLAN => License::STARTER_PLAN,
SILVER_PLAN => License::PREMIUM_PLAN,
GOLD_PLAN => License::ULTIMATE_PLAN,
EARLY_ADOPTER_PLAN => License::EARLY_ADOPTER_PLAN
}.freeze
prepended do prepended do
has_one :namespace_statistics, dependent: :destroy has_one :namespace_statistics, dependent: :destroy
delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset, delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
to: :namespace_statistics, allow_nil: true to: :namespace_statistics, allow_nil: true
validates :plan, inclusion: { in: EE_PLANS.keys }, allow_nil: true
end
# Checks features (i.e. https://about.gitlab.com/products/) availabily
# for a given Namespace plan. This method should consider ancestor groups
# being licensed.
def feature_available?(feature)
@features_available ||= Hash.new do |h, feature|
h[feature] = plans.any? { |plan| License.plan_includes_feature?(EE_PLANS[plan], feature) }
end
@features_available[feature]
end end
def actual_shared_runners_minutes_limit def actual_shared_runners_minutes_limit
...@@ -27,5 +52,16 @@ module EE ...@@ -27,5 +52,16 @@ module EE
shared_runners_minutes_limit_enabled? && shared_runners_minutes_limit_enabled? &&
shared_runners_minutes.to_i >= actual_shared_runners_minutes_limit shared_runners_minutes.to_i >= actual_shared_runners_minutes_limit
end end
private
def plans
@ancestors_plans ||=
if parent_id
ancestors.where.not(plan: nil).reorder(nil).pluck('DISTINCT plan') + [plan]
else
[plan]
end
end
end end
end end
...@@ -24,6 +24,17 @@ module EE ...@@ -24,6 +24,17 @@ module EE
!public? && shared_runners_enabled? && namespace.shared_runners_minutes_limit_enabled? !public? && shared_runners_enabled? && namespace.shared_runners_minutes_limit_enabled?
end end
# Checks licensed feature availability if `feature` matches any
# key on License::FEATURE_CODES. Otherwise, check feature availability
# through ProjectFeature.
def feature_available?(feature, user = nil)
if License::FEATURE_CODES.key?(feature)
licensed_feature_available?(feature)
else
super
end
end
def service_desk_address def service_desk_address
return nil unless service_desk_available? return nil unless service_desk_available?
...@@ -35,6 +46,17 @@ module EE ...@@ -35,6 +46,17 @@ module EE
private private
def licensed_feature_available?(feature)
globally_available = License.current&.feature_available?(feature)
if current_application_settings.should_check_namespace_plan?
globally_available &&
(public? && namespace.public? || namespace.feature_available?(feature))
else
globally_available
end
end
def service_desk_available? def service_desk_available?
return @service_desk_available if defined?(@service_desk_available) return @service_desk_available if defined?(@service_desk_available)
......
class License < ActiveRecord::Base class License < ActiveRecord::Base
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
DEPLOY_BOARD_FEATURE = 'GitLab_DeployBoard'.freeze
FILE_LOCK_FEATURE = 'GitLab_FileLocks'.freeze
GEO_FEATURE = 'GitLab_Geo'.freeze
AUDITOR_USER_FEATURE = 'GitLab_Auditor_User'.freeze
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
FEATURE_CODES = {
geo: GEO_FEATURE,
auditor_user: AUDITOR_USER_FEATURE,
service_desk: SERVICE_DESK_FEATURE,
# Features that make sense to Namespace:
deploy_board: DEPLOY_BOARD_FEATURE,
file_lock: FILE_LOCK_FEATURE
}.freeze
STARTER_PLAN = 'starter'.freeze
PREMIUM_PLAN = 'premium'.freeze
ULTIMATE_PLAN = 'ultimate'.freeze
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EES_FEATURES = [ EES_FEATURES = [
# .. # ..
].freeze ].freeze
EEP_FEATURES = [ EEP_FEATURES = [
*EES_FEATURES, *EES_FEATURES,
{ 'GitLab_DeployBoard' => 1 }, { DEPLOY_BOARD_FEATURE => 1 },
{ 'GitLab_FileLocks' => 1 }, { FILE_LOCK_FEATURE => 1 },
{ 'GitLab_Geo' => 1 }, { GEO_FEATURE => 1 },
{ 'GitLab_Auditor_User' => 1 }, { AUDITOR_USER_FEATURE => 1 },
{ 'GitLab_ServiceDesk' => 1 } { SERVICE_DESK_FEATURE => 1 }
].freeze
EEU_FEATURES = [
*EEP_FEATURES
# ..
].freeze
# List all features available for early adopters,
# i.e. users that started using GitLab.com before
# the introduction of Bronze, Silver, Gold plans.
# Obs.: Do not extend from other feature constants.
# Early adopters should not earn new features as they're
# introduced.
EARLY_ADOPTER_FEATURES = [
# TODO: Add EES features
# https://gitlab.com/gitlab-org/gitlab-ee/issues/2335)
{ DEPLOY_BOARD_FEATURE => 1 },
{ FILE_LOCK_FEATURE => 1 },
{ GEO_FEATURE => 1 },
{ AUDITOR_USER_FEATURE => 1 },
{ SERVICE_DESK_FEATURE => 1 }
].freeze ].freeze
FEATURES_BY_PLAN = { FEATURES_BY_PLAN = {
'starter' => EES_FEATURES, STARTER_PLAN => EES_FEATURES,
'premium' => EEP_FEATURES PREMIUM_PLAN => EEP_FEATURES,
ULTIMATE_PLAN => EEU_FEATURES,
EARLY_ADOPTER_PLAN => EARLY_ADOPTER_FEATURES
}.freeze }.freeze
validate :valid_license validate :valid_license
...@@ -48,6 +91,13 @@ class License < ActiveRecord::Base ...@@ -48,6 +91,13 @@ class License < ActiveRecord::Base
RequestStore.delete(:current_license) RequestStore.delete(:current_license)
end end
def plan_includes_feature?(plan, code)
features = features_for_plan(plan)
feature = FEATURE_CODES.fetch(code)
features[feature].to_i > 0
end
def block_changes? def block_changes?
!current || current.block_changes? !current || current.block_changes?
end end
...@@ -115,8 +165,9 @@ class License < ActiveRecord::Base ...@@ -115,8 +165,9 @@ class License < ActiveRecord::Base
explicit_add_ons.merge(plan_features) explicit_add_ons.merge(plan_features)
end end
def add_on?(code) def feature_available?(code)
add_ons[code].to_i > 0 feature = FEATURE_CODES.fetch(code)
add_ons[feature].to_i > 0
end end
def restricted_user_count def restricted_user_count
......
...@@ -76,7 +76,7 @@ class ProjectPolicy < BasePolicy ...@@ -76,7 +76,7 @@ class ProjectPolicy < BasePolicy
can! :read_deployment can! :read_deployment
can! :read_merge_request can! :read_merge_request
if License.current&.add_on?('GitLab_DeployBoard') || Rails.env.development? if project.feature_available?(:deploy_board) || Rails.env.development?
can! :read_deploy_board can! :read_deploy_board
end end
end end
......
.form-group
= f.label :plan, class: 'control-label'
.col-sm-10
= f.select :plan, options_for_select(Namespace::EE_PLANS.keys.map { |plan| [plan.titleize, plan] }, f.object.plan), {}, class: 'form-control'
...@@ -95,6 +95,16 @@ ...@@ -95,6 +95,16 @@
= f.check_box :user_default_external = f.check_box :user_default_external
Newly registered users will by default be external Newly registered users will by default be external
- if Gitlab.com? || Rails.env.development?
.form-group
= f.label :check_namespace_plan, 'Check feature availability on namespace plan', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :check_namespace_plan do
= f.check_box :check_namespace_plan
Enabling this will only make licensed EE features available to projects if the project namespace's plan
includes the feature or if the project is public.
%fieldset %fieldset
%legend Sign-up Restrictions %legend Sign-up Restrictions
.form-group .form-group
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
= render 'groups/repository_size_limit_setting', f: f = render 'groups/repository_size_limit_setting', f: f
- if current_application_settings.should_check_namespace_plan?
= render 'admin/namespace_plan', f: f
.form-group.group-description-holder .form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label' = f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -70,6 +70,12 @@ ...@@ -70,6 +70,12 @@
= f.label :note, 'Note', class: 'control-label' = f.label :note, 'Note', class: 'control-label'
.col-sm-10= f.text_area :note, class: 'form-control' .col-sm-10= f.text_area :note, class: 'form-control'
- if current_application_settings.should_check_namespace_plan?
= f.fields_for :namespace do |namespace_form|
%fieldset
%legend Plan
= render 'admin/namespace_plan', f: namespace_form
.form-actions .form-actions
- if @user.new_record? - if @user.new_record?
= f.submit 'Create user', class: "btn btn-create" = f.submit 'Create user', class: "btn btn-create"
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
= render 'projects/fork_suggestion' = render 'projects/fork_suggestion'
- if license_allows_file_locks? - if @project.feature_available?(:file_lock)
:javascript :javascript
PathLocks.init( PathLocks.init(
'#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}', '#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
= link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do
Charts Charts
- if license_allows_file_locks? - if @project.feature_available?(:file_lock)
= nav_link(controller: [:path_locks]) do = nav_link(controller: [:path_locks]) do
= link_to namespace_project_path_locks_path(@project.namespace, @project) do = link_to namespace_project_path_locks_path(@project.namespace, @project) do
Locked Files Locked Files
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
= render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post = render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir' = render 'projects/blob/new_dir'
- if license_allows_file_locks? - if @project.feature_available?(:file_lock)
:javascript :javascript
PathLocks.init( PathLocks.init(
'#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}', '#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPlanToNamespace < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def change
add_column(:namespaces, :plan, :string)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddCheckNamespacePlanToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
add_column_with_default :application_settings,
:check_namespace_plan,
:boolean,
default: false,
allow_null: false
end
def down
remove_column(:application_settings, :check_namespace_plan)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170511101000) do ActiveRecord::Schema.define(version: 20170512173638) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -137,6 +137,7 @@ ActiveRecord::Schema.define(version: 20170511101000) do ...@@ -137,6 +137,7 @@ ActiveRecord::Schema.define(version: 20170511101000) do
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.boolean "clientside_sentry_enabled", default: false, null: false t.boolean "clientside_sentry_enabled", default: false, null: false
t.string "clientside_sentry_dsn" t.string "clientside_sentry_dsn"
t.boolean "check_namespace_plan", default: false, null: false
end end
create_table "approvals", force: :cascade do |t| create_table "approvals", force: :cascade do |t|
...@@ -880,6 +881,7 @@ ActiveRecord::Schema.define(version: 20170511101000) do ...@@ -880,6 +881,7 @@ ActiveRecord::Schema.define(version: 20170511101000) do
t.boolean "require_two_factor_authentication", default: false, null: false t.boolean "require_two_factor_authentication", default: false, null: false
t.integer "two_factor_grace_period", default: 48, null: false t.integer "two_factor_grace_period", default: 48, null: false
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.string "plan"
end end
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
......
...@@ -474,6 +474,7 @@ module API ...@@ -474,6 +474,7 @@ module API
end end
class Namespace < Grape::Entity class Namespace < Grape::Entity
expose :plan, if: lambda { |_, options| options[:current_user] && options[:current_user].admin? }
expose :id, :name, :path, :kind, :full_path expose :id, :name, :path, :kind, :full_path
end end
......
...@@ -69,6 +69,14 @@ module API ...@@ -69,6 +69,14 @@ module API
end end
end end
def find_namespace(id)
if id =~ /^\d+$/
Namespace.find_by(id: id)
else
Namespace.find_by_full_path(id)
end
end
def find_group!(id) def find_group!(id)
group = find_group(id) group = find_group(id)
......
...@@ -19,6 +19,26 @@ module API ...@@ -19,6 +19,26 @@ module API
present paginate(namespaces), with: Entities::Namespace present paginate(namespaces), with: Entities::Namespace
end end
desc 'Update a namespace' do
success Entities::Namespace
end
params do
optional :plan, type: String, desc: "Namespace or Group plan"
end
put ':id' do
authenticated_as_admin!
namespace = find_namespace(params[:id])
return not_found!('Namespace') unless namespace
if namespace.update(declared_params)
present namespace, with: Entities::Namespace, current_user: current_user
else
render_validation_error!(namespace)
end
end
end end
end end
end end
...@@ -2,8 +2,7 @@ module EE ...@@ -2,8 +2,7 @@ module EE
module Gitlab module Gitlab
module ServiceDesk module ServiceDesk
def self.enabled? def self.enabled?
::License.current && ::License.current&.feature_available?(:service_desk) &&
::License.current.add_on?('GitLab_ServiceDesk') &&
::Gitlab::IncomingEmail.enabled? && ::Gitlab::IncomingEmail.enabled? &&
::Gitlab::IncomingEmail.supports_wildcard? ::Gitlab::IncomingEmail.supports_wildcard?
end end
......
require_dependency 'gitlab/git' require_dependency 'gitlab/git'
module Gitlab module Gitlab
SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
def self.com? def self.com?
# Check `staging?` as well to keep parity with gitlab.com # Check `gl_subdomain?` as well to keep parity with gitlab.com
Gitlab.config.gitlab.url == 'https://gitlab.com' || staging? Gitlab.config.gitlab.url == 'https://gitlab.com' || gl_subdomain?
end end
def self.staging? def self.gl_subdomain?
Gitlab.config.gitlab.url == 'https://staging.gitlab.com' SUBDOMAIN_REGEX === Gitlab.config.gitlab.url
end end
end end
...@@ -233,7 +233,7 @@ module Gitlab ...@@ -233,7 +233,7 @@ module Gitlab
end end
def validate_path_locks? def validate_path_locks?
@validate_path_locks ||= license_allows_file_locks? && @validate_path_locks ||= @project.feature_available?(:file_lock) &&
project.path_locks.any? && @newrev && @oldrev && project.path_locks.any? && @newrev && @oldrev &&
project.default_branch == @branch_name # locks protect default branch only project.default_branch == @branch_name # locks protect default branch only
end end
......
...@@ -47,7 +47,7 @@ module Gitlab ...@@ -47,7 +47,7 @@ module Gitlab
end end
def self.license_allows? def self.license_allows?
::License.current && ::License.current.add_on?('GitLab_Geo') ::License.current&.feature_available?(:geo)
end end
def self.primary? def self.primary?
......
...@@ -55,7 +55,7 @@ module Gitlab ...@@ -55,7 +55,7 @@ module Gitlab
end end
def service_desk_counts def service_desk_counts
return {} unless ::License.current&.add_on?('GitLab_ServiceDesk') return {} unless ::License.current&.feature_available?(:service_desk)
projects_with_service_desk = Project.where(service_desk_enabled: true) projects_with_service_desk = Project.where(service_desk_enabled: true)
......
...@@ -9,7 +9,7 @@ describe Projects::EnvironmentsController do ...@@ -9,7 +9,7 @@ describe Projects::EnvironmentsController do
end end
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_return(false) allow_any_instance_of(License).to receive(:feature_available?).and_return(false)
project.team << [user, :master] project.team << [user, :master]
...@@ -46,7 +46,7 @@ describe Projects::EnvironmentsController do ...@@ -46,7 +46,7 @@ describe Projects::EnvironmentsController do
context 'when requesting available environments scope' do context 'when requesting available environments scope' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(true)
get :index, environment_params(format: :json, scope: :available) get :index, environment_params(format: :json, scope: :available)
end end
...@@ -87,7 +87,7 @@ describe Projects::EnvironmentsController do ...@@ -87,7 +87,7 @@ describe Projects::EnvironmentsController do
context 'when license does not has the GitLab_DeployBoard add-on' do context 'when license does not has the GitLab_DeployBoard add-on' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(false)
get :index, environment_params(format: :json) get :index, environment_params(format: :json)
end end
...@@ -294,7 +294,7 @@ describe Projects::EnvironmentsController do ...@@ -294,7 +294,7 @@ describe Projects::EnvironmentsController do
let(:project) { create(:kubernetes_project) } let(:project) { create(:kubernetes_project) }
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(true)
allow_any_instance_of(Environment).to receive(:deployment_service_ready?).and_return(true) allow_any_instance_of(Environment).to receive(:deployment_service_ready?).and_return(true)
end end
...@@ -322,7 +322,7 @@ describe Projects::EnvironmentsController do ...@@ -322,7 +322,7 @@ describe Projects::EnvironmentsController do
context 'when license does not has the GitLab_DeployBoard add-on' do context 'when license does not has the GitLab_DeployBoard add-on' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(false)
end end
it 'does not return any data' do it 'does not return any data' do
......
...@@ -5,8 +5,8 @@ describe Projects::ServiceDeskController do ...@@ -5,8 +5,8 @@ describe Projects::ServiceDeskController do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
project.update(service_desk_enabled: true) project.update(service_desk_enabled: true)
......
...@@ -6,7 +6,7 @@ feature 'Path Locks', feature: true, js: true do ...@@ -6,7 +6,7 @@ feature 'Path Locks', feature: true, js: true do
let(:project_tree_path) { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } let(:project_tree_path) { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
before do before do
allow_any_instance_of(PathLocksHelper).to receive(:license_allows_file_locks?).and_return(true) allow(project).to receive(:feature_available?).with(:file_lock) { true }
project.team << [user, :master] project.team << [user, :master]
login_with(user) login_with(user)
......
...@@ -9,8 +9,8 @@ describe 'Service Desk Setting', js: true, feature: true do ...@@ -9,8 +9,8 @@ describe 'Service Desk Setting', js: true, feature: true do
before do before do
project.add_master(user) project.add_master(user)
login_as(user) login_as(user)
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
......
...@@ -35,7 +35,7 @@ describe TreeHelper do ...@@ -35,7 +35,7 @@ describe TreeHelper do
before do before do
allow(helper).to receive(:can?).and_return(true) allow(helper).to receive(:can?).and_return(true)
allow(helper).to receive(:current_user).and_return(user) allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:license_allows_file_locks?).and_return(true) allow(project).to receive(:feature_available?).with(:file_lock) { true }
project.reload project.reload
end end
......
...@@ -2,8 +2,8 @@ require 'spec_helper' ...@@ -2,8 +2,8 @@ require 'spec_helper'
describe EE::Gitlab::ServiceDesk, lib: true do describe EE::Gitlab::ServiceDesk, lib: true do
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
end end
...@@ -14,7 +14,7 @@ describe EE::Gitlab::ServiceDesk, lib: true do ...@@ -14,7 +14,7 @@ describe EE::Gitlab::ServiceDesk, lib: true do
context 'when license does not support service desk' do context 'when license does not support service desk' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { false }
end end
it { is_expected.to be_falsy } it { is_expected.to be_falsy }
......
...@@ -336,12 +336,11 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ...@@ -336,12 +336,11 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
let!(:path_lock) { create(:path_lock, path: 'README', project: project) } let!(:path_lock) { create(:path_lock, path: 'README', project: project) }
before do before do
allow_any_instance_of(PathLocksHelper).to receive(:license_allows_file_locks?).and_return(true)
allow(project.repository).to receive(:new_commits).and_return( allow(project.repository).to receive(:new_commits).and_return(
project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51') project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51')
) )
end end
it 'returns an error if the changes update a path locked by another user' do it 'returns an error if the changes update a path locked by another user' do
expect(subject.status).to be(false) expect(subject.status).to be(false)
expect(subject.message).to eq("The path 'README' is locked by #{path_lock.user.name}") expect(subject.message).to eq("The path 'README' is locked by #{path_lock.user.name}")
......
...@@ -19,8 +19,8 @@ describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -19,8 +19,8 @@ describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
allow(Notify).to receive(:service_desk_thank_you_email) allow(Notify).to receive(:service_desk_thank_you_email)
.with(kind_of(Integer)).and_return(double(deliver_later!: true)) .with(kind_of(Integer)).and_return(double(deliver_later!: true))
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
end end
...@@ -56,8 +56,8 @@ describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -56,8 +56,8 @@ describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
context 'when license does not support service desk' do context 'when license does not support service desk' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { false }
end end
it 'does not create an issue or send email' do it 'does not create an issue or send email' do
......
...@@ -13,15 +13,15 @@ describe Gitlab::Email::Handler, lib: true do ...@@ -13,15 +13,15 @@ describe Gitlab::Email::Handler, lib: true do
context 'a Service Desk email' do context 'a Service Desk email' do
it 'uses the Service Desk handler when Service Desk is enabled' do it 'uses the Service Desk handler when Service Desk is enabled' do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(true)
expect(handler_for('emails/service_desk.eml', 'some/project')).to be_instance_of(Gitlab::Email::Handler::EE::ServiceDeskHandler) expect(handler_for('emails/service_desk.eml', 'some/project')).to be_instance_of(Gitlab::Email::Handler::EE::ServiceDeskHandler)
end end
it 'uses no handler when Service Desk is disabled' do it 'uses no handler when Service Desk is disabled' do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(false)
expect(handler_for('emails/service_desk.eml', 'some/project')).to be_nil expect(handler_for('emails/service_desk.eml', 'some/project')).to be_nil
end end
...@@ -31,15 +31,15 @@ describe Gitlab::Email::Handler, lib: true do ...@@ -31,15 +31,15 @@ describe Gitlab::Email::Handler, lib: true do
let!(:user) { create(:user, email: 'jake@adventuretime.ooo', incoming_email_token: 'auth_token') } let!(:user) { create(:user, email: 'jake@adventuretime.ooo', incoming_email_token: 'auth_token') }
it 'uses the create issue handler when Service Desk is enabled' do it 'uses the create issue handler when Service Desk is enabled' do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(true)
expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler) expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end end
it 'uses the create issue handler when Service Desk is disabled' do it 'uses the create issue handler when Service Desk is disabled' do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(false)
expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler) expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end end
......
...@@ -88,12 +88,12 @@ describe Gitlab::Geo, lib: true do ...@@ -88,12 +88,12 @@ describe Gitlab::Geo, lib: true do
describe 'license_allows?' do describe 'license_allows?' do
it 'returns true if license has Geo addon' do it 'returns true if license has Geo addon' do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Geo') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:geo) { true }
expect(described_class.license_allows?).to be_truthy expect(described_class.license_allows?).to be_truthy
end end
it 'returns false if license doesnt have Geo addon' do it 'returns false if license doesnt have Geo addon' do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Geo') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:geo) { false }
expect(described_class.license_allows?).to be_falsey expect(described_class.license_allows?).to be_falsey
end end
......
...@@ -98,7 +98,7 @@ describe Gitlab::UsageData do ...@@ -98,7 +98,7 @@ describe Gitlab::UsageData do
context 'when Service Desk is disabled' do context 'when Service Desk is disabled' do
it 'returns an empty hash' do it 'returns an empty hash' do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(false)
expect(subject).to eq({}) expect(subject).to eq({})
end end
...@@ -115,7 +115,7 @@ describe Gitlab::UsageData do ...@@ -115,7 +115,7 @@ describe Gitlab::UsageData do
context 'when Service Desk is enabled' do context 'when Service Desk is enabled' do
it 'gathers Service Desk data' do it 'gathers Service Desk data' do
create_list(:issue, 3, confidential: true, author: User.support_bot, project: [project3, project4].sample) create_list(:issue, 3, confidential: true, author: User.support_bot, project: [project3, project4].sample)
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk).and_return(true)
expect(subject).to eq(service_desk_enabled_projects: 2, expect(subject).to eq(service_desk_enabled_projects: 2,
service_desk_issues: 3) service_desk_issues: 3)
......
...@@ -14,6 +14,10 @@ describe Gitlab, lib: true do ...@@ -14,6 +14,10 @@ describe Gitlab, lib: true do
expect(described_class.com?).to eq true expect(described_class.com?).to eq true
end end
it 'is true when on other gitlab subdomain' do
stub_config_setting(url: 'https://example.gitlab.com')
end
it 'is false when not on GitLab.com' do it 'is false when not on GitLab.com' do
stub_config_setting(url: 'http://example.com') stub_config_setting(url: 'http://example.com')
......
require 'spec_helper'
describe ApplicationSetting do
let(:setting) { described_class.create_from_defaults }
describe '#should_check_namespace_plan?' do
before do
stub_application_setting(check_namespace_plan: check_namespace_plan_column)
allow(::Gitlab).to receive(:com?) { gl_com }
end
subject { setting.should_check_namespace_plan? }
context 'when check_namespace_plan true AND on GitLab.com' do
let(:check_namespace_plan_column) { true }
let(:gl_com) { true }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when check_namespace_plan true AND NOT on GitLab.com' do
let(:check_namespace_plan_column) { true }
let(:gl_com) { false }
it 'returns false' do
is_expected.to eq(false)
end
end
context 'when check_namespace_plan false AND on GitLab.com' do
let(:check_namespace_plan_column) { false }
let(:gl_com) { true }
it 'returns false' do
is_expected.to eq(false)
end
end
end
end
...@@ -8,6 +8,41 @@ describe Namespace, models: true do ...@@ -8,6 +8,41 @@ describe Namespace, models: true do
it { is_expected.to delegate_method(:shared_runners_minutes).to(:namespace_statistics) } it { is_expected.to delegate_method(:shared_runners_minutes).to(:namespace_statistics) }
it { is_expected.to delegate_method(:shared_runners_seconds).to(:namespace_statistics) } it { is_expected.to delegate_method(:shared_runners_seconds).to(:namespace_statistics) }
it { is_expected.to delegate_method(:shared_runners_seconds_last_reset).to(:namespace_statistics) } it { is_expected.to delegate_method(:shared_runners_seconds_last_reset).to(:namespace_statistics) }
it { is_expected.to validate_inclusion_of(:plan).in_array(Namespace::EE_PLANS.keys).allow_nil }
describe '#feature_available?' do
let(:group) { create(:group, plan: plan_license) }
subject { group.feature_available?(feature) }
context 'when feature available' do
let(:feature) { :deploy_board }
let(:plan_license) { Namespace::GOLD_PLAN }
context 'when feature available for current group' do
it 'returns false' do
is_expected.to eq(true)
end
end
context 'when license is applied to parent group' do
let(:child_group) { create :group, parent: group }
it 'child group has feature available' do
expect(child_group.feature_available?(feature)).to eq(true)
end
end
end
context 'when feature not available' do
let(:feature) { :deploy_board }
let(:plan_license) { Namespace::BRONZE_PLAN }
it 'returns false' do
is_expected.to eq(false)
end
end
end
describe '#shared_runners_enabled?' do describe '#shared_runners_enabled?' do
subject { namespace.shared_runners_enabled? } subject { namespace.shared_runners_enabled? }
......
...@@ -11,6 +11,101 @@ describe Project, models: true do ...@@ -11,6 +11,101 @@ describe Project, models: true do
it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:namespace) } it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:namespace) }
end end
describe '#feature_available?' do
let(:namespace) { build_stubbed(:namespace) }
let(:project) { build_stubbed(:project, namespace: namespace) }
let(:user) { build_stubbed(:user) }
subject { project.feature_available?(feature, user) }
context 'when feature symbol is included on Namespace features code' do
before do
stub_application_setting('check_namespace_plan?' => check_namespace_plan)
allow(Gitlab).to receive(:com?) { true }
expect_any_instance_of(License).to receive(:feature_available?).with(feature) { allowed_on_global_license }
allow(namespace).to receive(:plan) { plan_license }
end
License::FEATURE_CODES.each do |feature_sym, feature_code|
let(:feature) { feature_sym }
let(:feature_code) { feature_code }
context "checking #{feature} availabily both on Global and Namespace license" do
let(:check_namespace_plan) { true }
context 'allowed by Plan License AND Global License' do
let(:allowed_on_global_license) { true }
let(:plan_license) { Namespace::GOLD_PLAN }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'not allowed by Plan License but project and namespace are public' do
let(:allowed_on_global_license) { true }
let(:plan_license) { Namespace::BRONZE_PLAN }
it 'returns true' do
allow(namespace).to receive(:public?) { true }
allow(project).to receive(:public?) { true }
is_expected.to eq(true)
end
end
context 'not allowed by Plan License' do
let(:allowed_on_global_license) { true }
let(:plan_license) { Namespace::BRONZE_PLAN }
it 'returns false' do
is_expected.to eq(false)
end
end
context 'not allowed by Global License' do
let(:allowed_on_global_license) { false }
let(:plan_license) { Namespace::GOLD_PLAN }
it 'returns false' do
is_expected.to eq(false)
end
end
end
context "when checking #{feature_code} only for Global license" do
let(:check_namespace_plan) { false }
context 'allowed by Global License' do
let(:allowed_on_global_license) { true }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'not allowed by Global License' do
let(:allowed_on_global_license) { false }
it 'returns false' do
is_expected.to eq(false)
end
end
end
end
end
context 'when feature symbol is not included on Namespace features code' do
let(:feature) { :issues }
it 'checks availability of licensed feature' do
expect(project.project_feature).to receive(:feature_available?).with(feature, user)
subject
end
end
end
describe '#any_runners_limit' do describe '#any_runners_limit' do
let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) } let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) }
let(:specific_runner) { create(:ci_runner) } let(:specific_runner) { create(:ci_runner) }
...@@ -120,8 +215,8 @@ describe Project, models: true do ...@@ -120,8 +215,8 @@ describe Project, models: true do
let(:project) { create(:empty_project, service_desk_enabled: true) } let(:project) { create(:empty_project, service_desk_enabled: true) }
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com") allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com")
end end
......
...@@ -218,7 +218,45 @@ describe License do ...@@ -218,7 +218,45 @@ describe License do
end end
it 'returns empty Hash if no features for given plan' do it 'returns empty Hash if no features for given plan' do
expect(described_class.features_for_plan('starter')).to eq({}) expect(described_class.features_for_plan('bronze')).to eq({})
end
end
describe '.plan_includes_feature?' do
let(:feature) { :deploy_board }
subject { described_class.plan_includes_feature?(plan, feature) }
context 'when addon included' do
let(:plan) { 'premium' }
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when addon not included' do
let(:plan) { 'starter' }
it 'returns false' do
is_expected.to eq(false)
end
end
context 'when plan is not set' do
let(:plan) { nil }
it 'returns false' do
is_expected.to eq(false)
end
end
context 'when feature does not exists' do
let(:plan) { 'premium' }
let(:feature) { nil }
it 'raises KeyError' do
expect { subject }.to raise_error(KeyError)
end
end end
end end
...@@ -307,7 +345,7 @@ describe License do ...@@ -307,7 +345,7 @@ describe License do
describe 'reading add-ons' do describe 'reading add-ons' do
describe '#add_ons' do describe '#add_ons' do
context "without add-ons" do context 'without add-ons' do
it 'returns an empty Hash' do it 'returns an empty Hash' do
license = build_license_with_add_ons({}) license = build_license_with_add_ons({})
...@@ -315,47 +353,53 @@ describe License do ...@@ -315,47 +353,53 @@ describe License do
end end
end end
context "with add-ons" do context 'with add-ons' do
it 'returns all available add-ons' do it 'returns all available add-ons' do
license = build_license_with_add_ons({ 'support' => 1, 'custom-domain' => 2 }) license = build_license_with_add_ons({ License::DEPLOY_BOARD_FEATURE => 1, License::FILE_LOCK_FEATURE => 2 })
expect(license.add_ons.keys).to include('support', 'custom-domain') expect(license.add_ons.keys).to include(License::DEPLOY_BOARD_FEATURE, License::FILE_LOCK_FEATURE)
end end
it 'can return details about a single add-on' do it 'can return details about a single add-on' do
license = build_license_with_add_ons({ 'custom-domain' => 2 }) license = build_license_with_add_ons({ License::DEPLOY_BOARD_FEATURE => 2 })
expect(license.add_ons['custom-domain']).to eq(2) expect(license.add_ons[License::DEPLOY_BOARD_FEATURE]).to eq(2)
end end
end end
context 'with extra features mapped by plan' do context 'with extra features mapped by plan' do
it 'returns all available add-ons and extra features' do it 'returns all available add-ons and extra features' do
license = build_license_with_add_ons({ 'support' => 1 }, plan: 'premium') license = build_license_with_add_ons({ License::DEPLOY_BOARD_FEATURE => 1 }, plan: License::PREMIUM_PLAN)
eep_features = License::EEP_FEATURES.reduce({}, :merge).keys eep_features = License::EEP_FEATURES.reduce({}, :merge).keys
expect(license.add_ons.keys).to include('support', *eep_features) expect(license.add_ons.keys).to include(License::DEPLOY_BOARD_FEATURE, *eep_features)
end end
end end
end end
describe '#add_on?' do describe '#feature_available?' do
it 'returns true if add-on exists and have a quantity greater than 0' do it 'returns true if add-on exists and have a quantity greater than 0' do
license = build_license_with_add_ons({ 'support' => 1 }) license = build_license_with_add_ons({ License::DEPLOY_BOARD_FEATURE => 1 })
expect(license.add_on?('support')).to eq(true) expect(license.feature_available?(:deploy_board)).to eq(true)
end end
it 'returns false if add-on exists but have a quantity of 0' do it 'returns false if add-on exists but have a quantity of 0' do
license = build_license_with_add_ons({ 'support' => 0 }) license = build_license_with_add_ons({ License::DEPLOY_BOARD_FEATURE => 0 })
expect(license.add_on?('support')).to eq(false) expect(license.feature_available?(:deploy_board)).to eq(false)
end end
it 'returns false if add-on does not exists' do it 'returns false if add-on does not exists' do
license = build_license_with_add_ons({}) license = build_license_with_add_ons({})
expect(license.add_on?('support')).to eq(false) expect(license.feature_available?(:deploy_board)).to eq(false)
end
it 'raises error if invalid symbol is sent' do
license = build_license_with_add_ons({})
expect { license.feature_available?(:invalid) }.to raise_error(KeyError)
end end
end end
......
...@@ -1671,7 +1671,7 @@ describe User, models: true do ...@@ -1671,7 +1671,7 @@ describe User, models: true do
before do before do
# `auditor?` returns true only when the user is an auditor _and_ the auditor license # `auditor?` returns true only when the user is an auditor _and_ the auditor license
# add-on is present. We aren't testing this here, so we can assume that the add-on exists. # add-on is present. We aren't testing this here, so we can assume that the add-on exists.
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { true }
end end
it 'does nothing for an invalid access level' do it 'does nothing for an invalid access level' do
...@@ -1751,7 +1751,7 @@ describe User, models: true do ...@@ -1751,7 +1751,7 @@ describe User, models: true do
context 'creating an auditor user' do context 'creating an auditor user' do
it "does not allow creating an auditor user if the addon isn't enabled" do it "does not allow creating an auditor user if the addon isn't enabled" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { false }
expect(build(:user, :auditor)).to be_invalid expect(build(:user, :auditor)).to be_invalid
end end
...@@ -1763,13 +1763,13 @@ describe User, models: true do ...@@ -1763,13 +1763,13 @@ describe User, models: true do
end end
it "allows creating an auditor user if the addon is enabled" do it "allows creating an auditor user if the addon is enabled" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { true }
expect(build(:user, :auditor)).to be_valid expect(build(:user, :auditor)).to be_valid
end end
it "allows creating a regular user if the addon isn't enabled" do it "allows creating a regular user if the addon isn't enabled" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { false }
expect(build(:user)).to be_valid expect(build(:user)).to be_valid
end end
...@@ -1777,25 +1777,25 @@ describe User, models: true do ...@@ -1777,25 +1777,25 @@ describe User, models: true do
context '#auditor?' do context '#auditor?' do
it "returns true for an auditor user if the addon is enabled" do it "returns true for an auditor user if the addon is enabled" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { true }
expect(build(:user, :auditor)).to be_auditor expect(build(:user, :auditor)).to be_auditor
end end
it "returns false for an auditor user if the addon is not enabled" do it "returns false for an auditor user if the addon is not enabled" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { false }
expect(build(:user, :auditor)).not_to be_auditor expect(build(:user, :auditor)).not_to be_auditor
end end
it "returns false for an auditor user if a license is not present" do it "returns false for an auditor user if a license is not present" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { false } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { false }
expect(build(:user, :auditor)).not_to be_auditor expect(build(:user, :auditor)).not_to be_auditor
end end
it "returns false for a non-auditor user even if the addon is present" do it "returns false for a non-auditor user even if the addon is present" do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_Auditor_User') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:auditor_user) { true }
expect(build(:user)).not_to be_auditor expect(build(:user)).not_to be_auditor
end end
......
...@@ -56,4 +56,48 @@ describe API::Namespaces do ...@@ -56,4 +56,48 @@ describe API::Namespaces do
end end
end end
end end
describe 'PUT /namespaces/:id' do
context 'when authenticated as admin' do
it 'updates plan using full_path' do
put api("/namespaces/#{group1.full_path}", admin), plan: 'silver'
expect(response).to have_http_status(200)
expect(json_response['plan']).to eq('silver')
end
it 'updates plan using id' do
put api("/namespaces/#{group1.id}", admin), plan: 'silver'
expect(response).to have_http_status(200)
expect(json_response['plan']).to eq('silver')
end
end
context 'when not authenticated as admin' do
it 'retuns 403' do
put api("/namespaces/#{group1.id}", user), plan: 'silver'
expect(response).to have_http_status(403)
end
end
context 'when namespace not found' do
it 'returns 404' do
put api("/namespaces/12345", admin), plan: 'silver'
expect(response).to have_http_status(404)
expect(json_response).to eq('message' => '404 Namespace Not Found')
end
end
context 'when invalid params' do
it 'returns validation error' do
put api("/namespaces/#{group1.id}", admin), plan: 'unknown'
expect(response).to have_http_status(400)
expect(json_response['message']).to eq('plan' => ['is not included in the list'])
end
end
end
end end
...@@ -11,7 +11,7 @@ describe EnvironmentEntity do ...@@ -11,7 +11,7 @@ describe EnvironmentEntity do
subject { entity.as_json } subject { entity.as_json }
before do before do
allow_any_instance_of(License).to receive(:add_on?).and_return(false) allow_any_instance_of(License).to receive(:feature_available?).and_return(false)
environment.project.team << [user, :master] environment.project.team << [user, :master]
end end
...@@ -46,7 +46,7 @@ describe EnvironmentEntity do ...@@ -46,7 +46,7 @@ describe EnvironmentEntity do
context 'with deployment service ready' do context 'with deployment service ready' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(true) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(true)
allow(environment).to receive(:deployment_service_ready?).and_return(true) allow(environment).to receive(:deployment_service_ready?).and_return(true)
end end
...@@ -59,7 +59,7 @@ describe EnvironmentEntity do ...@@ -59,7 +59,7 @@ describe EnvironmentEntity do
context 'when license does not has the GitLab_DeployBoard add-on' do context 'when license does not has the GitLab_DeployBoard add-on' do
before do before do
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_DeployBoard').and_return(false) allow_any_instance_of(License).to receive(:feature_available?).with(:deploy_board).and_return(false)
allow(environment).to receive(:deployment_service_ready?).and_return(true) allow(environment).to receive(:deployment_service_ready?).and_return(true)
end end
......
...@@ -7,8 +7,8 @@ describe EE::NotificationService do ...@@ -7,8 +7,8 @@ describe EE::NotificationService do
allow(Notify).to receive(:service_desk_new_note_email) allow(Notify).to receive(:service_desk_new_note_email)
.with(kind_of(Integer), kind_of(Integer)).and_return(double(deliver_later: true)) .with(kind_of(Integer), kind_of(Integer)).and_return(double(deliver_later: true))
allow_any_instance_of(License).to receive(:add_on?).and_call_original allow_any_instance_of(License).to receive(:feature_available?).and_call_original
allow_any_instance_of(License).to receive(:add_on?).with('GitLab_ServiceDesk') { true } allow_any_instance_of(License).to receive(:feature_available?).with(:service_desk) { true }
allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
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