Commit 5c68cc3f authored by Gosia Ksionek's avatar Gosia Ksionek Committed by Michael Kozono

Add specs for group template feature

Add to group spec namespace check
parent fe2aec39
...@@ -36,10 +36,10 @@ class ProjectsController < Projects::ApplicationController ...@@ -36,10 +36,10 @@ class ProjectsController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def new def new
namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id] @namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id]
return access_denied! if namespace && !can?(current_user, :create_projects, namespace) return access_denied! if @namespace && !can?(current_user, :create_projects, @namespace)
@project = Project.new(namespace_id: namespace&.id) @project = Project.new(namespace_id: @namespace&.id)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -27,7 +27,7 @@ module EE ...@@ -27,7 +27,7 @@ module EE
].tap do |params_ee| ].tap do |params_ee|
params_ee << { insight_attributes: :project_id } if current_group&.insights_available? params_ee << { insight_attributes: :project_id } if current_group&.insights_available?
params_ee << :file_template_project_id if current_group&.feature_available?(:custom_file_templates_for_namespace) params_ee << :file_template_project_id if current_group&.feature_available?(:custom_file_templates_for_namespace)
params_ee << :custom_project_templates_group_id if License.feature_available?(:custom_project_templates) params_ee << :custom_project_templates_group_id if current_group&.group_project_template_available?
end end
end end
......
# frozen_string_literal: true
class GroupsWithTemplatesFinder
# We need to provide grace period for users who are now using group_project_template
# feature in free groups.
CUT_OFF_DATE = Date.parse('2019/05/22') + 3.months
def initialize(group_id = nil)
@group_id = group_id
end
def execute
groups = @group_id ? ::Group.find(group_id).self_and_ancestors : ::Group.all
groups = groups.with_project_templates
if ::Gitlab::CurrentSettings.should_check_namespace_plan? && Time.zone.now > CUT_OFF_DATE
groups = groups.with_feature_available_in_plan(:group_project_templates)
end
groups
end
private
attr_reader :group_id
end
...@@ -166,6 +166,11 @@ module EE ...@@ -166,6 +166,11 @@ module EE
feature_available?(:multiple_group_issue_boards) feature_available?(:multiple_group_issue_boards)
end end
def group_project_template_available?
feature_available?(:group_project_templates) ||
(custom_project_templates_group_id? && Time.zone.now <= GroupsWithTemplatesFinder::CUT_OFF_DATE)
end
def actual_size_limit def actual_size_limit
return ::Gitlab::CurrentSettings.repository_size_limit if repository_size_limit.nil? return ::Gitlab::CurrentSettings.repository_size_limit if repository_size_limit.nil?
......
...@@ -38,6 +38,14 @@ module EE ...@@ -38,6 +38,14 @@ module EE
scope :with_plan, -> { where.not(plan_id: nil) } scope :with_plan, -> { where.not(plan_id: nil) }
scope :with_shared_runners_minutes_limit, -> { where("namespaces.shared_runners_minutes_limit > 0") } scope :with_shared_runners_minutes_limit, -> { where("namespaces.shared_runners_minutes_limit > 0") }
scope :with_extra_shared_runners_minutes_limit, -> { where("namespaces.extra_shared_runners_minutes_limit > 0") } scope :with_extra_shared_runners_minutes_limit, -> { where("namespaces.extra_shared_runners_minutes_limit > 0") }
scope :with_feature_available_in_plan, -> (feature) do
plans = plans_with_feature(feature)
matcher = Plan.where(name: plans)
.joins(:hosted_subscriptions)
.where("gitlab_subscriptions.namespace_id = namespaces.id")
.select('1')
where("EXISTS (?)", matcher)
end
delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset, delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
:extra_shared_runners_minutes, to: :namespace_statistics, allow_nil: true :extra_shared_runners_minutes, to: :namespace_statistics, allow_nil: true
......
...@@ -172,11 +172,11 @@ module EE ...@@ -172,11 +172,11 @@ module EE
end end
def available_subgroups_with_custom_project_templates(group_id = nil) def available_subgroups_with_custom_project_templates(group_id = nil)
groups = group_id ? ::Group.find(group_id).self_and_ancestors : ::Group.all groups = GroupsWithTemplatesFinder.new(group_id).execute
GroupsFinder.new(self, min_access_level: ::Gitlab::Access::MAINTAINER) GroupsFinder.new(self, min_access_level: ::Gitlab::Access::MAINTAINER)
.execute .execute
.where(id: groups.with_project_templates.select(:custom_project_templates_group_id)) .where(id: groups.select(:custom_project_templates_group_id))
.includes(:projects) .includes(:projects)
.reorder(nil) .reorder(nil)
.distinct .distinct
......
...@@ -69,6 +69,7 @@ class License < ApplicationRecord ...@@ -69,6 +69,7 @@ class License < ApplicationRecord
ci_cd_projects ci_cd_projects
protected_environments protected_environments
custom_project_templates custom_project_templates
group_project_templates
packages packages
code_owner_approval_required code_owner_approval_required
feature_flags feature_flags
......
- return unless ::Gitlab::CurrentSettings.custom_project_templates_enabled? - return unless @group.group_project_template_available?
- expanded = expanded_by_default? - expanded = expanded_by_default?
%section.settings.no-animate.qa-custom-project-templates{ class: ('expanded' if expanded) } %section.settings.no-animate.qa-custom-project-templates{ class: ('expanded' if expanded) }
......
---
title: Ensure custom group template feature is available only for groups on gold and silver
merge_request: 10678
author:
type: fixed
...@@ -188,7 +188,7 @@ describe 'Edit group settings' do ...@@ -188,7 +188,7 @@ describe 'Edit group settings' do
context 'is enabled' do context 'is enabled' do
before do before do
stub_licensed_features(custom_project_templates: true) stub_licensed_features(group_project_templates: true)
visit edit_group_path(selected_group) visit edit_group_path(selected_group)
end end
...@@ -207,9 +207,54 @@ describe 'Edit group settings' do ...@@ -207,9 +207,54 @@ describe 'Edit group settings' do
end end
end end
context 'namespace plan is checked' do
before do
create(:gitlab_subscription, namespace: group, hosted_plan: plan)
stub_licensed_features(group_project_templates: true)
allow(Gitlab::CurrentSettings.current_application_settings)
.to receive(:should_check_namespace_plan?) { true }
visit edit_group_path(selected_group)
end
context 'namespace is on the proper plan' do
let(:plan) { create(:gold_plan) }
context 'when the group is a top parent group' do
let(:selected_group) { group }
let(:nested_group) { subgroup }
it_behaves_like 'shows custom project templates settings'
end
context 'when the group is a subgroup' do
let(:selected_group) { subgroup }
let(:nested_group) { subgroup_1 }
it_behaves_like 'shows custom project templates settings'
end
end
context 'is disabled for namespace' do
let(:plan) { create(:bronze_plan) }
context 'when the group is the top parent group' do
let(:selected_group) { group }
it_behaves_like 'does not show custom project templates settings'
end
context 'when the group is a subgroup' do
let(:selected_group) { subgroup }
it_behaves_like 'does not show custom project templates settings'
end
end
end
context 'is disabled' do context 'is disabled' do
before do before do
stub_licensed_features(custom_project_templates: false) stub_licensed_features(group_project_templates: false)
visit edit_group_path(selected_group) visit edit_group_path(selected_group)
end end
......
...@@ -192,7 +192,7 @@ describe 'New project' do ...@@ -192,7 +192,7 @@ describe 'New project' do
context 'when licensed' do context 'when licensed' do
before do before do
stub_licensed_features(custom_project_templates: true) stub_licensed_features(custom_project_templates: true, group_project_templates: true)
end end
it 'shows Group tab in Templates section' do it 'shows Group tab in Templates section' do
...@@ -293,6 +293,7 @@ describe 'New project' do ...@@ -293,6 +293,7 @@ describe 'New project' do
before do before do
group1.add_owner(user) group1.add_owner(user)
group2.add_owner(user) group2.add_owner(user)
group3.add_owner(user)
group4.add_owner(user) group4.add_owner(user)
group1.update(custom_project_templates_group_id: subgroup1.id) group1.update(custom_project_templates_group_id: subgroup1.id)
group2.update(custom_project_templates_group_id: subgroup2.id) group2.update(custom_project_templates_group_id: subgroup2.id)
...@@ -354,6 +355,82 @@ describe 'New project' do ...@@ -354,6 +355,82 @@ describe 'New project' do
end end
end end
end end
context 'when namespace is supposed to be checked' do
context 'when in proper plan' do
context 'when creating project from top-level group with templates' do
let(:url) { new_project_path(namespace_id: group1.id) }
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
create(:gitlab_subscription, :gold, namespace: group1)
end
it 'show Group tab in Templates section' do
visit url
click_link 'Create from template'
expect(page).to have_css('.custom-group-project-templates-tab')
end
it_behaves_like 'group templates displayed' do
let(:template_number) { 2 }
end
end
context 'when creating project with templates' do
let(:url) { new_project_path(namespace_id: group1.id) }
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
create(:gitlab_subscription, :bronze, namespace: group1)
end
around do |example|
Timecop.freeze(GroupsWithTemplatesFinder::CUT_OFF_DATE - 1.day) do
example.run
end
end
it 'show Group tab in Templates section' do
visit url
click_link 'Create from template'
expect(page).to have_css('.custom-group-project-templates-tab')
end
it_behaves_like 'group templates displayed' do
let(:template_number) { 2 }
end
end
end
context 'when creating project with templates after grace period' do
let(:url) { new_project_path(namespace_id: group1.id) }
before do
stub_application_setting(check_namespace_plan: true)
create(:gitlab_subscription, :bronze, namespace: group1)
end
around do |example|
Timecop.freeze(GroupsWithTemplatesFinder::CUT_OFF_DATE + 1.day) do
example.run
end
end
it 'show Group tab in Templates section' do
visit url
click_link 'Create from template'
expect(page).to have_css('.custom-group-project-templates-tab')
end
it_behaves_like 'group templates displayed' do
let(:template_number) { 0 }
end
end
end
end end
context 'when group template is not set' do context 'when group template is not set' do
......
# frozen_string_literal: true
require 'spec_helper'
describe GroupsWithTemplatesFinder do
let(:group_1) { create(:group, name: 'group-1') }
let(:group_2) { create(:group, name: 'group-2') }
let(:group_3) { create(:group, name: 'group-3') }
let!(:group_4) { create(:group, name: 'group-4') }
let!(:subgroup_1) { create(:group, parent: group_1, name: 'subgroup-1') }
let!(:subgroup_2) { create(:group, parent: group_2, name: 'subgroup-2') }
let!(:subgroup_3) { create(:group, parent: group_3, name: 'subgroup-3') }
before do
group_1.update!(custom_project_templates_group_id: subgroup_1.id)
group_2.update!(custom_project_templates_group_id: subgroup_2.id)
group_3.update!(custom_project_templates_group_id: subgroup_3.id)
create(:project, namespace: subgroup_1)
create(:project, namespace: subgroup_2)
create(:project, namespace: subgroup_3)
create(:gitlab_subscription, :gold, namespace: group_1)
create(:gitlab_subscription, :silver, namespace: group_2)
end
describe 'without group id' do
it 'returns all groups' do
expect(described_class.new.execute).to contain_exactly(group_1, group_2, group_3)
end
context 'when namespace checked' do
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
end
it 'returns all groups before cut-off date' do
Timecop.freeze(described_class::CUT_OFF_DATE - 1.day) do
expect(described_class.new.execute).to contain_exactly(group_1, group_2, group_3)
end
end
it 'returns groups on gold/silver plan after cut-off date' do
Timecop.freeze(described_class::CUT_OFF_DATE + 1.day) do
expect(described_class.new.execute).to contain_exactly(group_1, group_2)
end
end
end
end
describe 'with group id' do
it 'returns given group with it descendants' do
expect(described_class.new(group_1.id).execute).to contain_exactly(group_1)
end
context 'when namespace checked' do
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
end
it 'returns given group with it descendants before cut-off date' do
Timecop.freeze(described_class::CUT_OFF_DATE - 1.day) do
expect(described_class.new(group_3.id).execute).to contain_exactly(group_3)
end
end
it 'does not return the group after the cut-off date' do
Timecop.freeze(described_class::CUT_OFF_DATE + 1.day) do
expect(described_class.new(group_3.id).execute).to be_empty
end
end
end
end
end
...@@ -289,78 +289,135 @@ describe Group do ...@@ -289,78 +289,135 @@ describe Group do
end end
describe 'Vulnerabilities::Occurrence collection methods' do describe 'Vulnerabilities::Occurrence collection methods' do
let(:project) { create(:project, namespace: group) }
let(:external_project) { create(:project) }
let(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
let!(:old_vuln) { create_vulnerability(project) }
let!(:new_vuln) { create_vulnerability(project) }
let!(:external_vuln) { create_vulnerability(external_project) }
let!(:failed_vuln) { create_vulnerability(project, failed_pipeline) }
def create_vulnerability(project, pipeline = nil)
pipeline ||= create(:ci_pipeline, :success, project: project)
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project)
end
describe '#latest_vulnerabilities' do describe '#latest_vulnerabilities' do
subject { group.latest_vulnerabilities } let(:project) { create(:project, namespace: group) }
let(:external_project) { create(:project) }
let(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do let!(:old_vuln) { create_vulnerability(project) }
is_expected.to contain_exactly(new_vuln) let!(:new_vuln) { create_vulnerability(project) }
let!(:external_vuln) { create_vulnerability(external_project) }
let!(:failed_vuln) { create_vulnerability(project, failed_pipeline) }
def create_vulnerability(project, pipeline = nil)
pipeline ||= create(:ci_pipeline, :success, project: project)
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project)
end end
context 'with vulnerabilities from other branches' do describe '#latest_vulnerabilities' do
let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') } subject { group.latest_vulnerabilities }
let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) }
# TODO: This should actually fail and we must scope vulns it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do
# per branch as soon as we store them for other branches is_expected.to contain_exactly(new_vuln)
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524 end
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln) context 'with vulnerabilities from other branches' do
let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') }
let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) }
# TODO: This should actually fail and we must scope vulns
# per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln)
end
end end
end end
end
describe '#latest_vulnerabilities_with_sha' do describe '#latest_vulnerabilities_with_sha' do
subject { group.latest_vulnerabilities_with_sha } subject { group.latest_vulnerabilities_with_sha }
it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do
is_expected.to contain_exactly(new_vuln)
end
it { is_expected.to all(respond_to(:sha)) }
context 'with vulnerabilities from other branches' do
let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') }
let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) }
it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do # TODO: This should actually fail and we must scope vulns
is_expected.to contain_exactly(new_vuln) # per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln)
end
end
end end
it { is_expected.to all(respond_to(:sha)) } describe '#all_vulnerabilities' do
subject { group.all_vulnerabilities }
context 'with vulnerabilities from other branches' do it 'returns vulns for all successful pipelines of projects belonging to the group' do
let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') } is_expected.to contain_exactly(old_vuln, new_vuln)
let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) } end
# TODO: This should actually fail and we must scope vulns context 'with vulnerabilities from other branches' do
# per branch as soon as we store them for other branches let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') }
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524 let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) }
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln) # TODO: This should actually fail and we must scope vulns
# per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(old_vuln, new_vuln, branch_vuln)
end
end end
end end
end end
end
describe '#group_project_template_available?' do
subject { group.group_project_template_available? }
context 'licensed' do
before do
stub_licensed_features(group_project_templates: true)
end
it 'returns true for licensed instance' do
is_expected.to be true
end
context 'when in need of checking plan' do
before do
allow(Gitlab::CurrentSettings.current_application_settings)
.to receive(:should_check_namespace_plan?) { true }
end
it 'returns true for groups in proper plan' do
create(:gitlab_subscription, namespace: group, hosted_plan: create(:gold_plan))
describe '#all_vulnerabilities' do is_expected.to be true
subject { group.all_vulnerabilities } end
it 'returns true for groups with group template already set within grace period' do
group.update!(custom_project_templates_group_id: create(:group, parent: group).id)
group.reload
Timecop.freeze(GroupsWithTemplatesFinder::CUT_OFF_DATE - 1.day) do
is_expected.to be true
end
end
it 'returns false for groups with group template already set after grace period' do
group.update!(custom_project_templates_group_id: create(:group, parent: group).id)
group.reload
it 'returns vulns for all successful pipelines of projects belonging to the group' do Timecop.freeze(GroupsWithTemplatesFinder::CUT_OFF_DATE + 1.day) do
is_expected.to contain_exactly(old_vuln, new_vuln) is_expected.to be false
end
end
end end
context 'with vulnerabilities from other branches' do context 'unlicensed' do
let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') } before do
let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) } stub_licensed_features(group_project_templates: false)
end
# TODO: This should actually fail and we must scope vulns it 'returns false unlicensed instance' do
# per branch as soon as we store them for other branches is_expected.to be false
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(old_vuln, new_vuln, branch_vuln)
end end
end end
end end
......
...@@ -50,6 +50,28 @@ describe Namespace do ...@@ -50,6 +50,28 @@ describe Namespace do
end end
end end
end end
describe '.with_feature_available_in_plan' do
let!(:namespace) { create :namespace, plan: namespace_plan }
context 'plan is nil' do
let(:namespace_plan) { nil }
it 'returns no namespace' do
expect(described_class.with_feature_available_in_plan(:group_project_templates)).to be_empty
end
end
context 'plan is set' do
let(:namespace_plan) { :bronze_plan }
it 'returns namespaces with plan' do
create(:gitlab_subscription, :bronze, namespace: namespace)
create(:gitlab_subscription, :free, namespace: create(:namespace))
expect(described_class.with_feature_available_in_plan(:audit_events)).to eq([namespace])
end
end
end
end end
describe 'custom validations' do describe 'custom validations' do
......
...@@ -402,6 +402,23 @@ describe User do ...@@ -402,6 +402,23 @@ describe User do
expect(groups.map(&:name)).not_to include('subgroup-3') expect(groups.map(&:name)).not_to include('subgroup-3')
end end
end end
context 'when namespace plan is checked' do
before do
create(:gitlab_subscription, namespace: group_1, hosted_plan: create(:bronze_plan))
create(:gitlab_subscription, namespace: group_2, hosted_plan: create(:gold_plan))
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
end
it 'returns groups on gold or silver plans' do
Timecop.freeze(GroupsWithTemplatesFinder::CUT_OFF_DATE + 1.day) do
groups = user.available_subgroups_with_custom_project_templates
expect(groups.size).to eq(1)
expect(groups.map(&:name)).to include('subgroup-2')
end
end
end
end end
end 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