Commit 1f3794ea authored by Allison Browne's avatar Allison Browne Committed by Fabio Pitino

Add Quota Presenter to separate Quota related display logic

parent 7c2fbd9e
......@@ -9,39 +9,14 @@ module Ci
class Quota
include Gitlab::Utils::StrongMemoize
Report = Struct.new(:used, :limit, :status)
attr_reader :namespace
def initialize(namespace)
@namespace = namespace
end
def enabled?
namespace_root? && total_minutes.nonzero?
end
# Status of the monthly allowance being used.
def monthly_minutes_report
Report.new(monthly_minutes_used, minutes_limit, report_status)
end
def monthly_percent_used
return 0 unless enabled?
return 0 if monthly_minutes == 0
100 * monthly_minutes_used.to_i / monthly_minutes
end
# Status of any purchased minutes used.
def purchased_minutes_report
status = purchased_minutes_used_up? ? :over_quota : :under_quota
Report.new(purchased_minutes_used, purchased_minutes, status)
end
def purchased_percent_used
return 0 unless enabled?
return 0 if purchased_minutes == 0
100 * purchased_minutes_used.to_i / purchased_minutes
namespace_root? && !namespace_unlimited_minutes?
end
def minutes_used_up?
......@@ -58,14 +33,6 @@ module Ci
total_minutes.to_i - total_minutes_used
end
def display_shared_runners_data?
namespace_root? && any_project_enabled?
end
def display_minutes_available_data?
display_shared_runners_data? && total_minutes.nonzero?
end
def total_minutes
strong_memoize(:total_minutes) do
monthly_minutes + purchased_minutes
......@@ -78,54 +45,43 @@ module Ci
end
end
def any_project_enabled?
strong_memoize(:any_project_enabled) do
namespace.any_project_with_shared_runners_enabled?
def purchased_minutes
strong_memoize(:purchased_minutes) do
namespace.extra_shared_runners_minutes_limit.to_i
end
end
private
attr_reader :namespace
def minutes_limit
return _('Not supported') unless display_shared_runners_data?
if display_minutes_available_data?
monthly_minutes
else
_('Unlimited')
def namespace_root?
strong_memoize(:namespace_root) do
namespace.root?
end
end
def report_status
return :disabled unless enabled?
monthly_minutes_used_up? ? :over_quota : :under_quota
def namespace_unlimited_minutes?
total_minutes.to_i == 0
end
def total_minutes_remaining
[current_balance, 0].max
def monthly_minutes
strong_memoize(:monthly_minutes) do
(namespace.shared_runners_minutes_limit || ::Gitlab::CurrentSettings.shared_runners_minutes).to_i
end
end
# === private to view ===
def monthly_minutes_used_up?
return false unless enabled?
monthly_minutes_used >= monthly_minutes
end
def purchased_minutes_used_up?
return false unless enabled?
any_minutes_purchased? && purchased_minutes_used >= purchased_minutes
end
def monthly_minutes_used
total_minutes_used - purchased_minutes_used
end
def monthly_minutes_available?
total_minutes_used <= monthly_minutes
def purchased_minutes_used_up?
return false unless enabled?
any_minutes_purchased? && purchased_minutes_used >= purchased_minutes
end
def purchased_minutes_used
......@@ -134,6 +90,12 @@ module Ci
total_minutes_used - monthly_minutes
end
private
def monthly_minutes_available?
total_minutes_used <= monthly_minutes
end
def no_minutes_purchased?
purchased_minutes == 0
end
......@@ -142,22 +104,9 @@ module Ci
purchased_minutes > 0
end
def monthly_minutes
strong_memoize(:monthly_minutes) do
(namespace.shared_runners_minutes_limit || ::Gitlab::CurrentSettings.shared_runners_minutes).to_i
end
end
def purchased_minutes
strong_memoize(:purchased_minutes) do
namespace.extra_shared_runners_minutes_limit.to_i
end
end
def namespace_root?
strong_memoize(:namespace_root) do
namespace.root?
end
# === private to model ===
def total_minutes_remaining
[current_balance, 0].max
end
end
end
......
# frozen_string_literal: true
module Ci
module Minutes
class QuotaPresenter < Gitlab::View::Presenter::Simple
include Gitlab::Utils::StrongMemoize
presents Quota, as: :quota
Report = Struct.new(:used, :limit, :status)
# Status of the monthly allowance being used.
def monthly_minutes_report
Report.new(quota.monthly_minutes_used, minutes_limit, report_status)
end
def monthly_percent_used
return 0 unless quota.enabled?
return 0 if quota.monthly_minutes == 0
100 * quota.monthly_minutes_used.to_i / quota.monthly_minutes
end
# Status of any purchased minutes used.
def purchased_minutes_report
status = quota.purchased_minutes_used_up? ? :over_quota : :under_quota
Report.new(quota.purchased_minutes_used, quota.purchased_minutes, status)
end
def purchased_percent_used
return 0 unless quota.enabled?
return 0 if quota.purchased_minutes == 0
100 * quota.purchased_minutes_used.to_i / quota.purchased_minutes
end
def display_minutes_available_data?
display_shared_runners_data? && !quota.namespace_unlimited_minutes?
end
def display_shared_runners_data?
quota.namespace_root? && any_project_enabled?
end
def any_project_enabled?
strong_memoize(:any_project_enabled) do
quota.namespace.any_project_with_shared_runners_enabled?
end
end
private
def report_status
return :disabled unless quota.enabled?
quota.monthly_minutes_used_up? ? :over_quota : :under_quota
end
def minutes_limit
return _('Not supported') unless display_shared_runners_data?
if display_minutes_available_data?
quota.monthly_minutes
else
_('Unlimited')
end
end
end
end
end
......@@ -2,11 +2,12 @@
- namespace = local_assigns.fetch(:namespace)
- minutes_quota = namespace.ci_minutes_quota
- minutes_quota_presenter = Ci::Minutes::QuotaPresenter.new(minutes_quota)
- return unless minutes_quota.enabled?
- if minutes_quota.display_shared_runners_data?
- if minutes_quota_presenter.display_shared_runners_data?
%li
%span.light= _('Additional minutes:')
%strong
= ci_minutes_report(minutes_quota.purchased_minutes_report)
= ci_minutes_report(minutes_quota_presenter.purchased_minutes_report)
- namespace = local_assigns.fetch(:namespace)
- minutes_quota = namespace.ci_minutes_quota
- minutes_quota_presenter = Ci::Minutes::QuotaPresenter.new(minutes_quota)
- if minutes_quota.display_shared_runners_data?
- if minutes_quota_presenter.display_shared_runners_data?
%li
%span.light= _('Pipeline minutes quota:')
%strong
= ci_minutes_report(minutes_quota.monthly_minutes_report)
= ci_minutes_report(minutes_quota_presenter.monthly_minutes_report)
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'shared-runners-pipeline-minutes-quota'), target: '_blank'
- return unless Gitlab.com?
- minutes_quota = namespace.ci_minutes_quota
- return unless minutes_quota.display_minutes_available_data? && minutes_quota.purchased_minutes_report.limit > 0
- minutes_quota_presenter = Ci::Minutes::QuotaPresenter.new(minutes_quota)
- return unless minutes_quota_presenter.display_minutes_available_data? && minutes_quota_presenter.purchased_minutes_report.limit > 0
.row
.col-sm-6
%strong
= _("Additional minutes")
%div
= ci_minutes_report(minutes_quota.purchased_minutes_report)
= ci_minutes_report(minutes_quota_presenter.purchased_minutes_report)
minutes
= link_to sprite_icon('question-o'), help_page_path('subscriptions/gitlab_com/index', anchor: 'purchase-additional-ci-minutes'), target: '_blank', rel: 'noopener noreferrer'
.col-sm-6.right
#{minutes_quota.purchased_percent_used}% used
= ci_minutes_progress_bar(minutes_quota.purchased_percent_used)
#{minutes_quota_presenter.purchased_percent_used}% used
= ci_minutes_progress_bar(minutes_quota_presenter.purchased_percent_used)
- namespace = locals.fetch(:namespace)
- projects = locals.fetch(:projects)
- minutes_quota = namespace.ci_minutes_quota
- minutes_quota_presenter = Ci::Minutes::QuotaPresenter.new(minutes_quota)
.pipeline-quota.container-fluid
.row
......@@ -21,19 +23,19 @@
- else
= s_('UsageQuota|Current period usage')
%div
= ci_minutes_report(minutes_quota.monthly_minutes_report)
= ci_minutes_report(minutes_quota_presenter.monthly_minutes_report)
minutes
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'shared-runners-pipeline-minutes-quota'), target: '_blank', 'aria-label': _('Shared runners help link')
.col-sm-6.right
- if minutes_quota.display_minutes_available_data?
#{minutes_quota.monthly_percent_used}% used
- elsif !minutes_quota.any_project_enabled?
- if minutes_quota_presenter.display_minutes_available_data?
#{minutes_quota_presenter.monthly_percent_used}% used
- elsif !minutes_quota_presenter.any_project_enabled?
0% used
- else
= s_('UsageQuota|Unlimited')
= ci_minutes_progress_bar(minutes_quota.monthly_percent_used)
= ci_minutes_progress_bar(minutes_quota_presenter.monthly_percent_used)
= render 'namespaces/pipelines_quota/extra_shared_runners_minutes_quota', namespace: namespace
......@@ -48,7 +50,7 @@
= _('Minutes')
%tbody
- if !minutes_quota.any_project_enabled?
- if !minutes_quota_presenter.any_project_enabled?
%tr
%td{ colspan: 2 }
.nothing-here-block
......
......@@ -51,9 +51,10 @@ RSpec.describe EE::NamespacesHelper do
describe '#ci_minutes_report' do
let(:quota) { Ci::Minutes::Quota.new(user_group) }
let(:quota_presenter) { Ci::Minutes::QuotaPresenter.new(quota) }
describe 'rendering monthly minutes report' do
let(:report) { quota.monthly_minutes_report }
let(:report) { quota_presenter.monthly_minutes_report }
context "when ci minutes quota is not enabled" do
before do
......@@ -62,7 +63,7 @@ RSpec.describe EE::NamespacesHelper do
context 'and the namespace is eligible for unlimited' do
before do
allow(quota).to receive(:namespace_eligible?).and_return(true)
allow(quota).to receive(:namespace_root?).and_return(true)
allow(user_group).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
end
......@@ -79,7 +80,7 @@ RSpec.describe EE::NamespacesHelper do
context 'and the namespace is not eligible for unlimited' do
before do
allow(quota).to receive(:namespace_eligible?).and_return(false)
allow(quota).to receive(:namespace_root?).and_return(false)
end
it 'returns Not supported for the limit section' do
......@@ -103,7 +104,7 @@ RSpec.describe EE::NamespacesHelper do
end
describe 'rendering purchased minutes report' do
let(:report) { quota.purchased_minutes_report }
let(:report) { Ci::Minutes::QuotaPresenter.new(quota).purchased_minutes_report }
context 'when extra minutes are assigned' do
it 'returns the proper values for used and limit sections' do
......
......@@ -63,244 +63,6 @@ RSpec.describe Ci::Minutes::Quota do
end
end
describe '#monthly_minutes_report' do
context 'when the quota is not enabled' do
before do
allow(quota).to receive(:enabled?).and_return(false)
allow(namespace).to receive(:root?).and_return(namespace_eligible)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
end
context 'when the namespace is not eligible' do
let(:namespace_eligible) { false }
it 'returns not supported report with no usage' do
report = quota.monthly_minutes_report
expect(report.limit).to eq 'Not supported'
expect(report.used).to eq 0
expect(report.status).to eq :disabled
end
end
context 'when the namespace is eligible' do
let(:namespace_eligible) { true }
context 'when minutes are not used' do
it 'returns unlimited report with no usage' do
report = quota.monthly_minutes_report
expect(report.limit).to eq 'Unlimited'
expect(report.used).to eq 0
expect(report.status).to eq :disabled
end
end
context 'when minutes are used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 20.minutes
end
it 'returns unlimited report with usage' do
report = quota.monthly_minutes_report
expect(report.limit).to eq 'Unlimited'
expect(report.used).to eq 20
expect(report.status).to eq :disabled
end
end
end
end
context 'when limited' do
before do
allow(quota).to receive(:enabled?).and_return(true)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
namespace.shared_runners_minutes_limit = 100
end
context 'when minutes are not all used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 30.minutes
end
it 'returns report with under quota' do
report = quota.monthly_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 30
expect(report.status).to eq :under_quota
end
end
context 'when minutes are all used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 101.minutes
end
it 'returns report with over quota' do
report = quota.monthly_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 101
expect(report.status).to eq :over_quota
end
end
end
end
describe '#purchased_minutes_report' do
context 'when limit enabled' do
before do
allow(quota).to receive(:enabled?).and_return(true)
namespace.shared_runners_minutes_limit = 200
end
context 'when extra minutes have been purchased' do
before do
namespace.extra_shared_runners_minutes_limit = 100
end
context 'when all monthly minutes are used and some puarchased minutes are used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 250.minutes
end
it 'returns report with under quota' do
report = quota.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 50
expect(report.status).to eq :under_quota
end
end
context 'when all monthly and all puarchased minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 301.minutes
end
it 'returns report with over quota' do
report = quota.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 101
expect(report.status).to eq :over_quota
end
end
context 'when not all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 190.minutes
end
it 'returns report with no usage' do
report = quota.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
end
context 'when no extra minutes have been purchased' do
before do
namespace.extra_shared_runners_minutes_limit = nil
end
context 'when all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 201.minutes
end
it 'returns report without usage' do
report = quota.purchased_minutes_report
expect(report.limit).to eq 0
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
context 'when not all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 190.minutes
end
it 'returns report with no usage' do
report = quota.purchased_minutes_report
expect(report.limit).to eq 0
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
end
end
end
describe '#monthly_percent_used' do
subject { quota.monthly_percent_used }
where(:limit_enabled, :monthly_limit, :purchased_limit, :minutes_used, :result, :title) do
false | 200 | 0 | 40 | 0 | 'limit not enabled'
true | 200 | 0 | 0 | 0 | 'monthly limit set and no usage'
true | 200 | 0 | 40 | 20 | 'monthly limit set and usage lower than 100%'
true | 200 | 0 | 200 | 100 | 'monthly limit set and usage at 100%'
true | 200 | 0 | 210 | 105 | 'monthly limit set and usage above 100%'
true | 0 | 0 | 0 | 0 | 'monthly limit not set and no usage'
true | 0 | 0 | 40 | 0 | 'monthly limit not set and some usage'
true | 200 | 100 | 0 | 0 | 'monthly and purchased limits set and no usage'
true | 200 | 100 | 40 | 20 | 'monthly and purchased limits set and low usage'
true | 200 | 100 | 210 | 100 | 'usage capped to 100% and overflows into purchased minutes'
end
with_them do
before do
allow(quota).to receive(:enabled?).and_return(limit_enabled)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
namespace.shared_runners_minutes_limit = monthly_limit
namespace.extra_shared_runners_minutes_limit = purchased_limit
namespace.namespace_statistics.shared_runners_seconds = minutes_used.minutes
end
it 'returns the percentage' do
is_expected.to eq result
end
end
end
describe '#purchased_percent_used' do
subject { quota.purchased_percent_used }
where(:limit_enabled, :monthly_limit, :purchased_limit, :minutes_used, :result, :title) do
false | 0 | 0 | 40 | 0 | 'limit not enabled'
true | 0 | 200 | 40 | 20 | 'monthly limit not set and purchased limit set and low usage'
true | 200 | 0 | 40 | 0 | 'monthly limit set and purchased limit not set and usage below monthly'
true | 200 | 0 | 240 | 0 | 'monthly limit set and purchased limit not set and usage above monthly'
true | 200 | 200 | 0 | 0 | 'monthly and purchased limits set and no usage'
true | 200 | 200 | 40 | 0 | 'monthly and purchased limits set and usage below monthly'
true | 200 | 200 | 200 | 0 | 'monthly and purchased limits set and monthly minutes maxed out'
true | 200 | 200 | 300 | 50 | 'monthly and purchased limits set and some purchased minutes used'
true | 200 | 200 | 400 | 100 | 'monthly and purchased limits set and all minutes used'
true | 200 | 200 | 430 | 115 | 'monthly and purchased limits set and usage beyond all limits'
end
with_them do
before do
allow(quota).to receive(:enabled?).and_return(limit_enabled)
namespace.shared_runners_minutes_limit = monthly_limit
namespace.extra_shared_runners_minutes_limit = purchased_limit
namespace.namespace_statistics.shared_runners_seconds = minutes_used.minutes
end
it 'returns the percentage' do
is_expected.to eq result
end
end
end
describe '#minutes_used_up?' do
subject { quota.minutes_used_up? }
......@@ -394,98 +156,85 @@ RSpec.describe Ci::Minutes::Quota do
end
end
describe '#any_project_enabled?' do
let_it_be(:project) { create(:project, namespace: namespace) }
describe '#monthly_minutes_used_up?' do
subject { quota.monthly_minutes_used_up? }
context 'when namespace has any project with shared runners enabled' do
before do
project.update!(shared_runners_enabled: true)
end
it 'returns true' do
expect(quota.any_project_enabled?).to be_truthy
end
end
context 'when quota is enabled' do
let(:total_minutes) { 1000 }
context 'when namespace has no projects with shared runners enabled' do
before do
project.update!(shared_runners_enabled: false)
end
it 'returns false' do
expect(quota.any_project_enabled?).to be_falsey
allow(namespace).to receive(:shared_runners_minutes_limit).and_return(total_minutes)
allow(namespace).to receive(:shared_runners_seconds).and_return(total_minutes_used * 60)
end
end
it 'does not trigger additional queries when called multiple times' do
# memoizes the result
quota.any_project_enabled?
context 'when monthly minutes quota greater than monthly minutes used' do
let(:total_minutes_used) { total_minutes - 1 }
# count
actual = ActiveRecord::QueryRecorder.new do
quota.any_project_enabled?
it { is_expected.to be_falsey }
end
expect(actual.count).to eq(0)
end
end
describe '#display_shared_runners_data?' do
let_it_be(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) }
context 'when monthly minutes quota less than monthly minutes used' do
let(:total_minutes_used) { total_minutes + 1 }
subject { quota.display_shared_runners_data? }
context 'when the namespace is root and it has a project with shared runners enabled' do
it { is_expected.to be_truthy }
end
it { is_expected.to be_truthy }
end
context 'when the namespace is not root' do
let(:namespace) { create(:group, :nested) }
context 'when monthly minutes quota equals monthly minutes used' do
let(:total_minutes_used) { total_minutes }
it { is_expected.to be_falsey }
it { is_expected.to be_truthy }
end
end
context 'when the namespaces has no project with shared runners enabled' do
context 'when quota is disabled' do
before do
project.update!(shared_runners_enabled: false)
allow(namespace).to receive(:shared_runners_minutes_limit).and_return(0)
end
it { is_expected.to be_falsey }
end
end
describe '#display_minutes_available_data?' do
let_it_be(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) }
subject { quota.display_minutes_available_data? }
context 'when the namespace is root and it has a project with shared runners enabled' do
context 'when there is a minutes limit' do
before do
namespace.update!(shared_runners_minutes_limit: 200)
end
describe 'purchased_minutes_used_up?' do
subject { quota.purchased_minutes_used_up? }
it { is_expected.to be_truthy }
context 'when quota is enabled' do
before do
allow(namespace).to receive(:shared_runners_minutes_limit).and_return(1000)
end
context 'when there is no minutes limit' do
context 'when no minutes are purchased' do
let(:purchased_minutes) { 0 }
before do
namespace.update!(shared_runners_minutes_limit: 0)
allow(namespace).to receive(:extra_shared_runners_minutes_limit).and_return(purchased_minutes)
end
it { is_expected.to be_falsey }
end
end
context 'when the namespace is not root' do
let(:namespace) { create(:group, :nested) }
context 'when minutes are purchased' do
where(:purchased_minutes, :monthly_minutes, :total_minutes_used, :result) do
1000 | 1000 | 2001 | true
1000 | 1000 | 2000 | true
1000 | 1000 | 1999 | false
end
it { is_expected.to be_falsey }
with_them do
before do
allow(namespace).to receive(:shared_runners_seconds).and_return(total_minutes_used * 60)
allow(namespace).to receive(:extra_shared_runners_minutes_limit).and_return(purchased_minutes)
allow(namespace).to receive(:shared_runners_minutes_limit).and_return(monthly_minutes)
end
it { is_expected.to eq(result) }
end
end
end
context 'when the namespaces has no project with shared runners enabled' do
context 'when quota is disabled' do
before do
project.update!(shared_runners_enabled: false)
allow(namespace).to receive(:shared_runners_minutes_limit).and_return(0)
end
it { is_expected.to be_falsey }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::Minutes::QuotaPresenter do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:namespace) do
create(:group, namespace_statistics: create(:namespace_statistics))
end
let(:quota) { Ci::Minutes::Quota.new(namespace) }
subject(:presenter) { described_class.new(quota) }
describe '#monthly_minutes_report' do
context 'when the quota is not enabled' do
before do
allow(quota).to receive(:enabled?).and_return(false)
allow(namespace).to receive(:root?).and_return(namespace_eligible)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
end
context 'when the namespace is not eligible' do
let(:namespace_eligible) { false }
it 'returns not supported report with no usage' do
report = presenter.monthly_minutes_report
expect(report.limit).to eq 'Not supported'
expect(report.used).to eq 0
expect(report.status).to eq :disabled
end
end
context 'when the namespace is eligible' do
let(:namespace_eligible) { true }
context 'when minutes are not used' do
it 'returns unlimited report with no usage' do
report = presenter.monthly_minutes_report
expect(report.limit).to eq 'Unlimited'
expect(report.used).to eq 0
expect(report.status).to eq :disabled
end
end
context 'when minutes are used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 20.minutes
end
it 'returns unlimited report with usage' do
report = presenter.monthly_minutes_report
expect(report.limit).to eq 'Unlimited'
expect(report.used).to eq 20
expect(report.status).to eq :disabled
end
end
end
end
context 'when limited' do
before do
allow(presenter).to receive(:enabled?).and_return(true)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
namespace.shared_runners_minutes_limit = 100
end
context 'when minutes are not all used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 30.minutes
end
it 'returns report with under quota' do
report = presenter.monthly_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 30
expect(report.status).to eq :under_quota
end
end
context 'when minutes are all used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 101.minutes
end
it 'returns report with over quota' do
report = presenter.monthly_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 101
expect(report.status).to eq :over_quota
end
end
end
end
describe '#purchased_minutes_report' do
context 'when limit enabled' do
before do
allow(quota).to receive(:enabled?).and_return(true)
namespace.shared_runners_minutes_limit = 200
end
context 'when extra minutes have been purchased' do
before do
namespace.extra_shared_runners_minutes_limit = 100
end
context 'when all monthly minutes are used and some puarchased minutes are used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 250.minutes
end
it 'returns report with under quota' do
report = presenter.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 50
expect(report.status).to eq :under_quota
end
end
context 'when all monthly and all puarchased minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 301.minutes
end
it 'returns report with over quota' do
report = presenter.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 101
expect(report.status).to eq :over_quota
end
end
context 'when not all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 190.minutes
end
it 'returns report with no usage' do
report = presenter.purchased_minutes_report
expect(report.limit).to eq 100
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
end
context 'when no extra minutes have been purchased' do
before do
namespace.extra_shared_runners_minutes_limit = nil
end
context 'when all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 201.minutes
end
it 'returns report without usage' do
report = presenter.purchased_minutes_report
expect(report.limit).to eq 0
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
context 'when not all monthly minutes have been used' do
before do
namespace.namespace_statistics.shared_runners_seconds = 190.minutes
end
it 'returns report with no usage' do
report = presenter.purchased_minutes_report
expect(report.limit).to eq 0
expect(report.used).to eq 0
expect(report.status).to eq :under_quota
end
end
end
end
end
describe '#monthly_percent_used' do
subject { presenter.monthly_percent_used }
where(:limit_enabled, :monthly_limit, :purchased_limit, :minutes_used, :result, :title) do
false | 200 | 0 | 40 | 0 | 'limit not enabled'
true | 200 | 0 | 0 | 0 | 'monthly limit set and no usage'
true | 200 | 0 | 40 | 20 | 'monthly limit set and usage lower than 100%'
true | 200 | 0 | 200 | 100 | 'monthly limit set and usage at 100%'
true | 200 | 0 | 210 | 105 | 'monthly limit set and usage above 100%'
true | 0 | 0 | 0 | 0 | 'monthly limit not set and no usage'
true | 0 | 0 | 40 | 0 | 'monthly limit not set and some usage'
true | 200 | 100 | 0 | 0 | 'monthly and purchased limits set and no usage'
true | 200 | 100 | 40 | 20 | 'monthly and purchased limits set and low usage'
true | 200 | 100 | 210 | 100 | 'usage capped to 100% and overflows into purchased minutes'
end
with_them do
before do
allow(quota).to receive(:enabled?).and_return(limit_enabled)
allow(namespace).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
namespace.shared_runners_minutes_limit = monthly_limit
namespace.extra_shared_runners_minutes_limit = purchased_limit
namespace.namespace_statistics.shared_runners_seconds = minutes_used.minutes
end
it 'returns the percentage' do
is_expected.to eq result
end
end
end
describe '#purchased_percent_used' do
subject { presenter.purchased_percent_used }
where(:limit_enabled, :monthly_limit, :purchased_limit, :minutes_used, :result, :title) do
false | 0 | 0 | 40 | 0 | 'limit not enabled'
true | 0 | 200 | 40 | 20 | 'monthly limit not set and purchased limit set and low usage'
true | 200 | 0 | 40 | 0 | 'monthly limit set and purchased limit not set and usage below monthly'
true | 200 | 0 | 240 | 0 | 'monthly limit set and purchased limit not set and usage above monthly'
true | 200 | 200 | 0 | 0 | 'monthly and purchased limits set and no usage'
true | 200 | 200 | 40 | 0 | 'monthly and purchased limits set and usage below monthly'
true | 200 | 200 | 200 | 0 | 'monthly and purchased limits set and monthly minutes maxed out'
true | 200 | 200 | 300 | 50 | 'monthly and purchased limits set and some purchased minutes used'
true | 200 | 200 | 400 | 100 | 'monthly and purchased limits set and all minutes used'
true | 200 | 200 | 430 | 115 | 'monthly and purchased limits set and usage beyond all limits'
end
with_them do
before do
allow(quota).to receive(:enabled?).and_return(limit_enabled)
namespace.shared_runners_minutes_limit = monthly_limit
namespace.extra_shared_runners_minutes_limit = purchased_limit
namespace.namespace_statistics.shared_runners_seconds = minutes_used.minutes
end
it 'returns the percentage' do
is_expected.to eq result
end
end
end
describe '#any_project_enabled?' do
let_it_be(:project) { create(:project, namespace: namespace) }
context 'when namespace has any project with shared runners enabled' do
before do
project.update!(shared_runners_enabled: true)
end
it 'returns true' do
expect(presenter.any_project_enabled?).to be_truthy
end
end
context 'when namespace has no projects with shared runners enabled' do
before do
project.update!(shared_runners_enabled: false)
end
it 'returns false' do
expect(presenter.any_project_enabled?).to be_falsey
end
end
it 'does not trigger additional queries when called multiple times' do
# memoizes the result
presenter.any_project_enabled?
# count
actual = ActiveRecord::QueryRecorder.new do
presenter.any_project_enabled?
end
expect(actual.count).to eq(0)
end
end
describe '#display_shared_runners_data?' do
let_it_be(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) }
subject { presenter.send('display_shared_runners_data?') }
context 'when the namespace is root and it has a project with shared runners enabled' do
it { is_expected.to be_truthy }
end
context 'when the namespace is not root' do
let(:namespace) { create(:group, :nested) }
it { is_expected.to be_falsey }
end
context 'when the namespaces has no project with shared runners enabled' do
before do
project.update!(shared_runners_enabled: false)
end
it { is_expected.to be_falsey }
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