ee/spec/models/license_spec.rb (1,725 lines of code) (raw):

# frozen_string_literal: true require "spec_helper" RSpec.describe License, feature_category: :plan_provisioning do using RSpec::Parameterized::TableSyntax subject(:license) { build(:license, data: gl_license.export) } let(:gl_license) { build(:gitlab_license) } let_it_be(:gl_licensee) do { 'Name' => 'Team Member', 'Email' => 'team_member@gitlab.com', 'Company' => 'GitLab' } end def build_license_with_add_ons(add_ons, plan: nil) gl_license = build(:gitlab_license, restrictions: { add_ons: add_ons, plan: plan }) build(:license, data: gl_license.export) end describe 'validations' do describe '#valid_license' do subject(:license) { build(:license, data: gl_license.class.encryptor.encrypt(gl_license.to_json)) } context 'when the license is provided' do shared_examples 'an invalid license' do it 'adds an error' do expect(license).not_to be_valid expect(license.errors.full_messages.to_sentence).to include error_message end end context 'with online cloud license' do let(:gl_license) { build(:gitlab_license, :cloud, starts_at: 'not-a-date') } let(:error_message) { 'The license key is invalid.' } it_behaves_like 'an invalid license' end context 'with offline cloud license' do let(:gl_license) { build(:gitlab_license, :offline, starts_at: 'not-a-date') } let(:error_message) do _('The license key is invalid. Make sure it is exactly as you received it from GitLab Inc.') end it_behaves_like 'an invalid license' end it { is_expected.to be_valid } end context 'when no license is provided' do before do license.data = nil end it 'adds an error' do expect(license).not_to be_valid expect(license.errors.full_messages.to_sentence) .to include _('The license key is invalid. Make sure it is exactly as you received it from GitLab Inc.') end end end describe '#check_trueup' do let(:active_user_count) { described_class.current.daily_billable_users_count + 10 } let(:date) { described_class.current.starts_at } before do create(:historical_data, recorded_at: date, active_user_count: active_user_count) end shared_examples 'invalid if active users with threshold exceeds restricted user count' do before do create_list(:user, 12) end it 'is not valid' do expect(license).not_to be_valid expect(license.errors.added?(:base, :check_available_seats)).to eq(true) end end context 'when reconciliation_completed is true on the license' do before do set_restrictions(seats: 10, trueup_quantity: 8, reconciliation_completed: true, previous_user_count: 0) end it { is_expected.to be_valid } end context 'when reconciliation_completed is false on the license' do it 'adds errors for invalid true up figures' do set_restrictions(seats: 10, trueup_quantity: 8, reconciliation_completed: false, trueup_period_seat_count: 0) expect(license).not_to be_valid expect(license.errors.added?(:base, :check_trueup)).to eq(true) expect(license.errors.full_messages.to_sentence) .to include 'You have applied a True-up for 8 users but you need one for 10 users' end end context 'when reconciliation_completed is not present on the license' do it 'adds errors for invalid true up figures' do set_restrictions(seats: 10, trueup_quantity: 8, trueup_period_seat_count: 0) expect(license).not_to be_valid expect(license.errors.added?(:base, :check_trueup)).to eq(true) expect(license.errors.full_messages.to_sentence) .to include 'You have applied a True-up for 8 users but you need one for 10 users' end end context 'when trueup quantity with threshold is more than the required quantity' do before do set_restrictions(seats: 10, trueup_quantity: 10, trueup_period_seat_count: 0) end it { is_expected.to be_valid } it_behaves_like 'invalid if active users with threshold exceeds restricted user count' end context 'when trueup quantity with threshold is equal to the required quantity' do before do set_restrictions(seats: 10, trueup_quantity: 10, trueup_period_seat_count: 0) end let(:active_user_count) { described_class.current.daily_billable_users_count + 11 } it { is_expected.to be_valid } it_behaves_like 'invalid if active users with threshold exceeds restricted user count' end context 'when trueup quantity with threshold is less than the required quantity' do before do set_restrictions(seats: 10, trueup_quantity: 8, trueup_period_seat_count: 0) end it 'is not valid' do expect(license).not_to be_valid expect(license.errors.added?(:base, :check_trueup)).to eq(true) end end context 'when trueup_period_seat_count is absent and previous_user_count is absent' do before do set_restrictions(seats: 10, trueup_quantity: 10) end it { is_expected.to be_valid } it_behaves_like 'invalid if active users with threshold exceeds restricted user count' end context 'when trueup_period_seat_count is present' do before do set_restrictions(seats: 5, trueup_quantity: 6, trueup_period_seat_count: 4) end it { is_expected.to be_valid } end context 'when trueup_period_seat_count is absent but previous_user_count is present' do before do set_restrictions(seats: 5, trueup_quantity: 6, previous_user_count: 4) end it { is_expected.to be_valid } it_behaves_like 'invalid if active users with threshold exceeds restricted user count' end context 'with license types' do subject(:license) { build(:license, cloud: cloud, data: gl_license.export) } let(:restrictions) do { trueup_quantity: 10, trueup_from: 1.year.ago.to_s, trueup_to: 1.day.ago.to_s } end context 'when license is an online cloud license' do let(:cloud) { true } let(:gl_license) { build(:gitlab_license, :online, restrictions: restrictions) } it 'does not validate for true-ups' do expect(license).not_to receive(:check_trueup) license.valid? end end context 'when license is an offline cloud license' do let(:cloud) { true } let(:gl_license) { build(:gitlab_license, :offline, restrictions: restrictions) } it 'does not validate for true-ups' do expect(license).not_to receive(:check_trueup) license.valid? end end context 'when license is a legacy license' do let(:cloud) { false } let(:gl_license) { build(:gitlab_license, restrictions: restrictions) } it 'validates for true-ups' do expect(license).to receive(:check_trueup) license.valid? end end end end describe '#check_available_seats' do context 'when reconciliation_completed is true' do before do set_restrictions(seats: 10, reconciliation_completed: true) create_list(:user, user_count) create(:historical_data, recorded_at: described_class.current.starts_at, active_user_count: 100) end context 'when seats with threshold is more than active_user_count' do let(:user_count) { 10 } it { is_expected.to be_valid } end context 'when seats with threshold is equal than active_user_count' do let(:user_count) { 11 } it { is_expected.to be_valid } end context 'when the seats with threshold is less than active_user_count' do let(:user_count) { 12 } it 'add limit error' do expect(license.valid?).to be_falsey expect(license.errors.full_messages.to_sentence).to include( 'This GitLab installation currently has 12 active users, exceeding this license\'s limit of 10 by 2 users' ) expect(license.errors.full_messages.to_sentence).not_to include( 'During the year before this license started' ) end it 'validates only if the new record' do expect(license.valid?).to be_falsey license.save!(validate: false) expect(license.valid?).to be_truthy end context 'when the license is cloud-enabled' do before do gl_license.cloud_licensing_enabled = true end it { is_expected.to be_valid } end end end context 'when reconciliation_completed is false' do context 'when the seats with threshold is less than active_user_count' do before do set_restrictions(seats: 10, reconciliation_completed: false) create_list(:user, 12) create(:historical_data, recorded_at: described_class.current.starts_at, active_user_count: 100) end it 'add limit error' do expect(license.valid?).to be_falsey expect(license.errors.full_messages.to_sentence).to include( 'During the year before this license started' ) end context 'when the license is cloud-enabled' do before do gl_license.cloud_licensing_enabled = true end it { is_expected.to be_valid } end end end end describe '#check_users_limit' do let(:expires_at) { 11.months.from_now.to_date } let(:restrictions) { { active_user_count: 9 } } let(:gl_license) do build( :gitlab_license, starts_at: 1.month.ago.to_date, expires_at: expires_at, restrictions: restrictions ) end def create_historical_data(recorded_at, prior_active_user_count) create( :historical_data, recorded_at: recorded_at + 1.day, active_user_count: 1 ) create( :historical_data, recorded_at: recorded_at, active_user_count: prior_active_user_count ) end context 'for each plan' do before do create(:group_member, :guest) create(:group_member, :reporter) create(:license, plan: plan) end let(:users_count) { nil } let(:new_license) do gl_license = build(:gitlab_license, restrictions: { plan: plan, active_user_count: users_count, previous_user_count: 1 }) build(:license, data: gl_license.export) end where(:gl_plan, :valid) do ::License::STARTER_PLAN | false ::License::PREMIUM_PLAN | false ::License::ULTIMATE_PLAN | true end with_them do let(:plan) { gl_plan } context 'when license has restricted users' do let(:users_count) { 1 } it { expect(new_license.valid?).to eq(valid) } end context 'when license has unlimited users' do let(:users_count) { nil } it 'is always valid' do expect(new_license.valid?).to eq(true) end end end end context 'when license is a cloud license' do let(:gitlab_license) do build( :gitlab_license, cloud_licensing_enabled: true, starts_at: Date.current, restrictions: { active_user_count: 10 } ) end it { is_expected.to be_valid } end context 'when no restriction is set' do let(:restrictions) { {} } it { is_expected.to be_valid } end context 'without historical data' do let(:active_user_count) { 9 } before do create_list(:user, billable_users_count) end context 'with previous user count' do let(:prior_active_user_count) { 0 } let(:restrictions) { { active_user_count: active_user_count, previous_user_count: previous_user_count } } context 'when prior historical max is less than previous user count' do let(:previous_user_count) { 1 } include_examples 'valid daily billable users count compared to limit set by license checks' include_examples 'invalid daily billable users count compared to limit set by license checks' end context 'when prior historical max is equal to previous user count' do let(:previous_user_count) { 0 } include_examples 'valid daily billable users count compared to limit set by license checks' include_examples 'invalid daily billable users count compared to limit set by license checks' end end context 'without previous user count' do let(:restrictions) { { active_user_count: active_user_count } } include_examples 'valid prior historical max compared to limit set by license checks' end end context 'with historical data in the term of an existing current license' do let(:active_user_count) { 9 } before do create_list(:user, billable_users_count) create_historical_data(described_class.current.expires_at, prior_active_user_count) end context 'with previous user count' do let(:previous_user_count) { 7 } let(:restrictions) { { active_user_count: active_user_count, previous_user_count: previous_user_count } } include_examples 'with previous user count checks' end context 'without previous user count' do let(:restrictions) { { active_user_count: active_user_count } } include_examples 'valid prior historical max compared to limit set by license checks' include_examples 'invalid prior historical max compared to limit set by license checks' end end context 'with historical data in the term of the new license (no current license exists)' do let(:active_user_count) { 9 } let(:restrictions) { { active_user_count: active_user_count } } before do create_list(:user, billable_users_count) allow(described_class).to receive(:current).and_return(nil) end context 'when new license has an expiration date' do before do create_historical_data(license.starts_at - 1.year, prior_active_user_count) end context 'with previous user count' do let(:previous_user_count) { 7 } let(:restrictions) { { active_user_count: active_user_count, previous_user_count: previous_user_count } } include_examples 'with previous user count checks' end context 'without previous user count' do let(:restrictions) { { active_user_count: active_user_count } } include_examples 'valid prior historical max compared to limit set by license checks' include_examples 'invalid prior historical max compared to limit set by license checks' end end context 'when new license has no expiration' do let(:expires_at) { nil } before do gl_license.licensee = gl_licensee create_historical_data(license.starts_at, prior_active_user_count) end context 'with previous user count' do let(:previous_user_count) { 7 } let(:restrictions) { { active_user_count: active_user_count, previous_user_count: previous_user_count } } include_examples 'with previous user count checks' end context 'without previous user count' do let(:restrictions) { { active_user_count: active_user_count } } include_examples 'valid prior historical max compared to limit set by license checks' include_examples 'invalid prior historical max compared to limit set by license checks' end end end context 'downgrade' do context 'when more users were added in previous period' do before do create(:historical_data, recorded_at: described_class.current.starts_at + 1.month, active_user_count: 15) set_restrictions(seats: 5, previous_user_count: 10) end it 'is invalid without a true-up' do expect(license).not_to be_valid end end context 'when no users were added in the previous period' do before do create(:historical_data, recorded_at: 6.months.ago, active_user_count: 15) set_restrictions(seats: 10, previous_user_count: 15) end it { is_expected.to be_valid } end end end describe '#not_expired' do context "when the license doesn't expire" do it { is_expected.to be_valid } end context 'when the license has expired' do before do gl_license.expires_at = Date.yesterday end it { is_expected.not_to be_valid } context 'when the license is an online cloud license' do before do gl_license.cloud_licensing_enabled = true gl_license.offline_cloud_licensing_enabled = false end context 'when the license is generated_from_cancellation' do before do gl_license.generated_from_cancellation = true end it { is_expected.to be_valid } end context 'when the license is not generated_from_cancellation' do before do gl_license.generated_from_cancellation = false end it { is_expected.not_to be_valid } end end end context 'when the license has yet to expire' do before do gl_license.expires_at = Date.tomorrow end it { is_expected.to be_valid } end end end describe 'Callbacks' do describe '#reset_current', :request_store do def current_license_cached_value License.cache.read(License::CACHE_KEY, License) end before do described_class.current # Set cache up front end context 'when a license is created' do it 'expires the current_license cached value' do expect(current_license_cached_value).to be_present create(:license) expect(current_license_cached_value).to be_nil end end context 'when a license is updated' do it 'expires the current_license cached value' do expect(current_license_cached_value).to be_present described_class.last.update!(updated_at: Time.current) expect(current_license_cached_value).to be_nil end end context 'when a license is destroyed' do it 'expires the current_license cached value' do expect(current_license_cached_value).to be_present described_class.last.destroy! expect(current_license_cached_value).to be_nil end end end describe '#reset_future_dated', :request_store do let!(:future_dated_license) { create(:license, data: create(:gitlab_license, starts_at: Date.current + 1.month).export) } before do described_class.future_dated expect(Gitlab::SafeRequestStore.read(:future_dated_license)).to be_present end context 'when a license is created' do it 'deletes the future_dated_license value in Gitlab::SafeRequestStore' do create(:license) expect(Gitlab::SafeRequestStore.read(:future_dated_license)).to be_nil end end context 'when a license is destroyed' do it 'deletes the future_dated_license value in Gitlab::SafeRequestStore' do future_dated_license.destroy! expect(Gitlab::SafeRequestStore.read(:future_dated_license)).to be_nil end end end end describe 'Scopes' do describe '.cloud' do it 'includes cloud licenses' do create(:license) cloud_license_1 = create(:license, cloud: true) cloud_license_2 = create(:license, cloud: true) result = described_class.cloud expect(result).to contain_exactly(cloud_license_1, cloud_license_2) end end end describe "Class methods" do before do described_class.reset_current end describe '.current', :request_store, :use_clean_rails_memory_store_caching do context 'when licenses table does not exist' do it 'returns nil' do allow(described_class).to receive(:table_exists?).and_return(false) expect(described_class.current).to be_nil end end context 'when there is no license' do it 'returns nil' do allow(described_class).to receive(:last_hundred).and_return([]) expect(described_class.current).to be_nil end end context 'when the license is invalid' do it 'returns nil' do allow(described_class).to receive(:last_hundred).and_return([license]) allow(license).to receive(:valid?).and_return(false) expect(described_class.current).to be_nil end end context 'when the license is valid' do let!(:current_license) { create_list(:license, 2).last } let(:expired_gl_license) do create(:gitlab_license, starts_at: Date.current - 1.month, expires_at: Date.yesterday) end context 'when the most recent valid started but not expired license matches the subscription name of a generated_from_cancellation license' do let!(:current_license) { create(:license, cloud: true, data: active_gl_license.export) } let(:active_gl_license) do build( :gitlab_license, starts_at: Date.current - 1.month, cloud_licensing_enabled: true, restrictions: { subscription_name: "SUB-001" } ) end let(:outdated_active_gl_license) do build( :gitlab_license, starts_at: Date.current - 1.month, cloud_licensing_enabled: true, restrictions: { subscription_name: "SUB-002" } ) end let(:generated_from_cancellation_gl_license) do build( :gitlab_license, starts_at: Date.current - 1.month, expires_at: Date.yesterday, cloud_licensing_enabled: true, generated_from_cancellation: true, restrictions: { subscription_name: "SUB-002" } ) end it 'returns the most recent valid and started but not expired license from a different subscription name' do create(:license, cloud: true, data: outdated_active_gl_license.export) create(:license, cloud: true, data: generated_from_cancellation_gl_license.export) expect(described_class.current).to eq(current_license) end context 'when all licenses match the subscription name of a generated_from_cancellation license', :without_license do let!(:current_license) { create(:license, cloud: true, data: outdated_active_gl_license.export) } it 'returns the most recent valid started expired license' do generated_from_cancellation_license = create(:license, cloud: true, data: generated_from_cancellation_gl_license.export) expect(described_class.current).to eq(generated_from_cancellation_license) end end end context 'when the last uploaded license is expired' do it 'returns the most recent valid and started but not expired license' do create(:license, data: expired_gl_license.export) expect(described_class.current).to eq(current_license) end end context 'when all uploaded license are expired', :without_license do let!(:current_license) { create_list(:license, 2, data: expired_gl_license.export).last } it 'returns the most recent valid and started and expired license' do expect(described_class.current).to eq(current_license) end end context 'when the last uploaded license is future dated' do it 'returns the most recent valid and started but not expired license' do create(:license, data: create(:gitlab_license, starts_at: Date.current + 1.month).export) expect(described_class.current).to eq(current_license) end end it 'returns the most recent valid and started but not expired license' do expect(described_class.current).to eq(current_license) end it 'caches the license' do described_class.reset_current expect(described_class).to receive(:load_license).once.and_call_original 2.times do expect(described_class.current).to eq(current_license) end travel_to(61.seconds.from_now) do expect(described_class).to receive(:load_license).once.and_call_original expect(described_class.current).to eq(current_license) end end end end describe '.future_dated' do before do described_class.reset_future_dated end context 'when licenses table does not exist' do it 'returns nil' do allow(described_class).to receive(:table_exists?).and_return(false) expect(described_class.future_dated).to be_nil end end context 'when there is no license' do it 'returns nil' do allow(described_class).to receive(:last_hundred).and_return([]) expect(described_class.future_dated).to be_nil end end context 'when the license is invalid' do it 'returns false' do license = build(:license, data: build(:gitlab_license, starts_at: Date.current + 1.month).export) allow(described_class).to receive(:last_hundred).and_return([license]) allow(license).to receive(:valid?).and_return(false) expect(described_class.future_dated).to be_nil end end context 'when the license is valid' do it 'returns the true' do future_dated_license = create(:license, data: create(:gitlab_license, starts_at: Date.current + 1.month).export) expect(described_class.future_dated).to eq(future_dated_license) end end end describe ".block_changes?" do before do allow(described_class).to receive(:current).and_return(license) end context "when there is no current license" do let(:license) { nil } it "returns false" do expect(described_class.block_changes?).to be_falsey end end context 'with an expired trial license' do let!(:license) { create(:license, trial: true) } it 'returns false' do expect(described_class.block_changes?).to be_falsey end end context 'with an expired normal license' do let!(:license) { create(:license, expired: true) } it 'returns true' do expect(described_class.block_changes?).to eq(true) end end context "when the current license is set to block changes" do before do allow(license).to receive(:block_changes?).and_return(true) end it "returns true" do expect(described_class.block_changes?).to be_truthy end end context "when the current license doesn't block changes" do it "returns false" do expect(described_class.block_changes?).to be_falsey end end end describe '.with_valid_license' do context 'when license trial' do before do allow(license).to receive(:trial?).and_return(true) allow(described_class).to receive(:current).and_return(license) end it 'does not yield block' do expect { |b| described_class.with_valid_license(&b) }.not_to yield_control end end context 'when license nil' do before do allow(described_class).to receive(:current).and_return(nil) end it 'does not yield block' do expect { |b| described_class.with_valid_license(&b) }.not_to yield_control end end context 'when license is valid' do before do allow(described_class).to receive(:current).and_return(license) end it 'yields block' do expect { |b| described_class.with_valid_license(&b) }.to yield_with_args(license) end end end describe '.current_cloud_license?' do subject { described_class.current_cloud_license?(license_key) } let(:license_key) { 'test-key' } before do allow(described_class).to receive(:current).and_return(current_license) end context 'when current license is not set' do let(:current_license) { nil } it { is_expected.to be(false) } end context 'when current license is not a cloud license' do let(:current_license) { create(:license) } it { is_expected.to be(false) } end context 'when current license is a cloud license but key does not match current' do let(:current_license) { create_current_license(cloud_licensing_enabled: true) } it { is_expected.to be(false) } end context 'when current license is a cloud license and key matches current' do let(:current_license) { create_current_license(cloud_licensing_enabled: true) } let(:license_key) { current_license.data } it { is_expected.to be(true) } end end end describe "#data_filename" do subject { license.data_filename } context 'when licensee includes company information' do let(:gl_license) do build(:gitlab_license, licensee: { 'Company' => ' Example & Partner Inc. 2 ', 'Name' => 'User Example' }) end it { is_expected.to eq('ExamplePartnerInc2.gitlab-license') } end context 'when licensee does not include company information' do let(:gl_license) { build(:gitlab_license, licensee: { 'Name' => 'User Example' }) } it { is_expected.to eq('UserExample.gitlab-license') } end end describe '#normalized_data' do it 'replaces carriage returns' do other_license = build(:license, data: license.data.gsub("\n", "\r\n")) expect(other_license.normalized_data).not_to include("\r\n") end it 'adds a trailing newline' do other_license = build(:license, data: license.data.chomp) expect(other_license.normalized_data).to end_with("\n") end it 'replaces multiple trailing newlines with a single trailing newline' do other_license = build(:license, data: "#{license.data}\n\n\n") expect(other_license.normalized_data).to end_with(/\n{1}$/) end end describe "#md5", fips_mode: false do it "returns the same MD5 for licenses with carriage returns and those without" do other_license = build(:license, data: license.data.gsub("\n", "\r\n")) expect(other_license.md5).to eq(license.md5) end it "returns the same MD5 for licenses with trailing newlines and those without" do other_license = build(:license, data: license.data.chomp) expect(other_license.md5).to eq(license.md5) end it "returns the same MD5 for licenses with multiple trailing newlines and those with a single trailing newline" do other_license = build(:license, data: "#{license.data}\n\n\n") expect(other_license.md5).to eq(license.md5) end context 'when in FIPS mode', :fips_mode do it "returns nil" do expect(license.md5).to eq(nil) end end end describe "#sha256" do it "returns the same SHA256 for licenses with carriage returns and those without" do other_license = build(:license, data: license.data.gsub("\n", "\r\n")) expect(other_license.sha256).to eq(license.sha256) end it "returns the same SHA256 for licenses with trailing newlines and those without" do other_license = build(:license, data: license.data.chomp) expect(other_license.sha256).to eq(license.sha256) end it "returns the same SHA256 for licenses with multiple trailing newlines and those with a single trailing newline" do other_license = build(:license, data: "#{license.data}\n\n\n") expect(other_license.sha256).to eq(license.sha256) end end describe "#license" do context "when no data is provided" do before do license.data = nil end it "returns nil" do expect(license.license).to be_nil end end context "when corrupt license data is provided" do before do license.data = "whatever" end it "returns nil" do expect(license.license).to be_nil end end context "when valid license data is provided" do it "returns the license" do expect(license.license).not_to be_nil end end end describe 'reading add-ons' do describe '#plan' do let(:gl_license) { build(:gitlab_license, restrictions: restrictions.merge(add_ons: {})) } let(:license) { build(:license, data: gl_license.export) } subject { license.plan } [ { restrictions: {}, plan: License::STARTER_PLAN }, { restrictions: { plan: nil }, plan: License::STARTER_PLAN }, { restrictions: { plan: '' }, plan: License::STARTER_PLAN }, { restrictions: { plan: 'unknown' }, plan: 'unknown' } ].each do |spec| context spec.inspect do let(:restrictions) { spec[:restrictions] } it { is_expected.to eq(spec[:plan]) } end end end end def create_license(add_ons: {}, plan: nil) gl_license = create(:gitlab_license, restrictions: { add_ons: add_ons, plan: plan }) create(:license, data: gl_license.export) end # rubocop: disable Gitlab/FeatureAvailableUsage # Disabling Cop because we are testing the instance method instead of the class method # and it's a valid usage. describe '.feature_available?' do subject { described_class.feature_available?(feature) } it 'returns true if add-on exists and have a quantity greater than 0' do create_license(add_ons: { 'GitLab_FileLocks' => 1 }) expect(described_class.feature_available?(:file_locks)).to eq(true) end it 'returns true if the feature is included in the plan do' do create_license(plan: License::PREMIUM_PLAN) expect(described_class.feature_available?(:auditor_user)).to eq(true) end it 'returns false if add-on exists but have a quantity of 0' do create_license(add_ons: { 'GitLab_FileLocks' => 0 }) expect(described_class.feature_available?(:file_locks)).to eq(false) end it 'returns false if add-on does not exists' do create_license(plan: License::STARTER_PLAN) expect(described_class.feature_available?(:file_locks)).to eq(false) end context 'with an expired trial license' do before_all do described_class.delete_all create(:license, trial: true, expired: true) end ::GitlabSubscriptions::Features::ALL_STARTER_FEATURES.each do |feature| it "returns false for #{feature}" do expect(described_class.feature_available?(feature)).to eq(false) end end end describe 'usage ping features' do context 'without license' do before do described_class.delete_all end context 'when usage ping is disabled' do before do stub_application_setting(usage_ping_features_enabled: false) end it 'does not have access to any usage ping features' do ::GitlabSubscriptions::Features::FEATURES_WITH_USAGE_PING.each do |feature| expect(described_class.feature_available?(feature)) .to eq(false), "expected #{feature} not to be available" end end end context 'when usage ping is enabled' do before do stub_application_setting(usage_ping_features_enabled: true) end it 'has access to all usage ping features' do ::GitlabSubscriptions::Features::FEATURES_WITH_USAGE_PING.each do |feature| expect(described_class.feature_available?(feature)) .to eq(true), "expected #{feature} to be available" end end end end context 'when license exists' do before do create(:license, plan: License::PREMIUM_PLAN) end context 'when usage ping is disabled' do before do stub_application_setting(usage_ping_features_enabled: false) end it 'has access to usage ping features part of the plan' do ::GitlabSubscriptions::Features::ALL_PREMIUM_FEATURES.each do |feature| expect(described_class.feature_available?(feature)).to eq(true) end end it 'does not have access to higher tier usage ping features' do ::GitlabSubscriptions::Features::ULTIMATE_FEATURES_WITH_USAGE_PING.each do |feature| expect(described_class.feature_available?(feature)).to eq(false) end end end context 'when usage ping is enabled' do before do stub_application_setting(usage_ping_features_enabled: true) end it 'has access to all usage ping features' do ::GitlabSubscriptions::Features::FEATURES_WITH_USAGE_PING.each do |feature| expect(described_class.feature_available?(feature)).to eq(true) end end end end end end # rubocop: enable Gitlab/FeatureAvailableUsage describe '.ai_features_available?' do using RSpec::Parameterized::TableSyntax subject { described_class.ai_features_available? } where(:plan, :ai_features_available) do License::STARTER_PLAN | false License::PREMIUM_PLAN | true License::ULTIMATE_PLAN | true end with_them do before do create_license(plan: plan) end it { is_expected.to be(ai_features_available) } end end describe '.duo_core_features_available?' do using RSpec::Parameterized::TableSyntax subject { described_class.duo_core_features_available? } where(:plan, :ai_features_available) do License::STARTER_PLAN | false License::PREMIUM_PLAN | true License::ULTIMATE_PLAN | true end with_them do before do create_license(plan: plan) end it { is_expected.to be(ai_features_available) } end end describe '#subscription_id' do it 'has correct subscription_id' do gl_license = build(:gitlab_license, restrictions: { subscription_id: "1111" }) license = build(:license, data: gl_license.export) expect(license.subscription_id).to eq("1111") end end describe '#subscription_name' do it 'returns the subscription_name from the license restrictions' do gl_license = build(:gitlab_license, restrictions: { subscription_name: 'SUB-001' }) license = build(:license, data: gl_license.export) expect(license.subscription_name).to eq('SUB-001') end end describe '#daily_billable_users_count' do before_all do create(:group_member) create(:group_member, user: create(:admin)) create(:group_member, :guest) create(:group_member, user: create(:user, :bot)) create(:group_member, user: create(:user, :project_bot)) create(:group_member, user: create(:user, :ghost)) create(:group_member).user.deactivate! end context 'when license is not for Ultimate plan' do it 'includes guests in the count' do expect(license.daily_billable_users_count).to eq(3) end end context 'when license is for Ultimate plan' do it 'excludes guests in the count' do new_license = create(:license, plan: License::ULTIMATE_PLAN) expect(new_license.daily_billable_users_count).to eq(2) end end end describe '#daily_billable_users_updated_time' do before do freeze_time end context 'when a billable_users usage trend measurement is available' do it 'returns the same recorded_at value' do create(:usage_trends_measurement, identifier: :billable_users, count: 2, recorded_at: '2012-10-11T09:15:15Z') expect(license.daily_billable_users_updated_time).to eq('2012-10-11 09:15:15 UTC') end end context 'without a billable_users usage trend measurement record' do it 'returns Time.zone.now value' do expect(license.daily_billable_users_updated_time).to eq(Time.zone.now.to_s) end end end describe '#overage' do it 'returns 0 if seats is nil' do allow(license).to receive(:seats) { nil } expect(license.overage).to eq(0) end it 'returns the difference between user_count and seats' do allow(license).to receive(:seats) { 10 } expect(license.overage(14)).to eq(4) end it 'returns the difference using daily_billable_users_count as user_count if no user_count argument provided' do allow(license).to receive(:daily_billable_users_count) { 110 } allow(license).to receive(:seats) { 100 } expect(license.overage).to eq(10) end it 'returns 0 if the difference is a negative number' do allow(license).to receive(:seats) { 2 } expect(license.overage(1)).to eq(0) end end describe '#historical_data' do subject(:historical_data_count) { license.historical_data.count } let_it_be(:now) { DateTime.new(2014, 12, 15) } let_it_be(:license) { create(:license, starts_at: Date.new(2014, 7, 1), expires_at: Date.new(2014, 12, 31)) } before_all do (1..12).each do |i| create(:historical_data, recorded_at: Date.new(2014, i, 1), active_user_count: i * 100) end create(:historical_data, recorded_at: license.starts_at - 1.day, active_user_count: 1) create(:historical_data, recorded_at: license.expires_at + 1.day, active_user_count: 2) create(:historical_data, recorded_at: now - 1.year - 1.day, active_user_count: 3) create(:historical_data, recorded_at: now + 1.day, active_user_count: 4) end around do |example| travel_to(now) { example.run } end context 'with using parameters' do it 'returns correct number of records within the given range' do from = Date.new(2014, 8, 1) to = Date.new(2014, 11, 30) expect(license.historical_data(from: from, to: to).count).to eq(4) end end context 'with a license that has a start and end date' do it 'returns correct number of records within the license range' do expect(historical_data_count).to eq(7) end end context 'with a license that has no end date' do let_it_be(:license) { create(:license, starts_at: Date.new(2014, 7, 1), expires_at: nil) } it 'returns correct number of records from the license\'s start date to today' do expect(historical_data_count).to eq(6) end end end describe '#historical_max' do subject(:historical_max) { license.historical_max } let(:license) { create(:license, starts_at: Date.current - 1.month, expires_at: Date.current + 1.month) } context 'when using parameters' do before do (1..12).each do |i| create(:historical_data, recorded_at: Date.new(2014, i, 1), active_user_count: i * 100) end end it 'returns max user count for the given time range' do from = Date.new(2014, 6, 1) to = Date.new(2014, 9, 1) expect(license.historical_max(from: from, to: to)).to eq(900) end end context 'with different plans for the license' do using RSpec::Parameterized::TableSyntax where(:gl_plan, :expected_count) do ::License::STARTER_PLAN | 2 ::License::PREMIUM_PLAN | 2 ::License::ULTIMATE_PLAN | 1 end with_them do let(:plan) { gl_plan } let(:license) do create(:license, plan: plan, starts_at: Date.current - 1.month, expires_at: Date.current + 1.month) end before do license create(:group_member, :guest) create(:group_member, :reporter) HistoricalData.track! end it 'does not count guest users' do expect(historical_max).to eq(expected_count) end end end context 'with data inside and outside of the license period' do before do create(:historical_data, recorded_at: license.starts_at.ago(2.days), active_user_count: 20) create(:historical_data, recorded_at: license.starts_at.in(2.days), active_user_count: 10) create(:historical_data, recorded_at: license.starts_at.in(5.days), active_user_count: 15) create(:historical_data, recorded_at: license.expires_at.in(2.days), active_user_count: 25) end it 'returns max value for active_user_count for within the license period only' do expect(historical_max).to eq(15) end end context 'when license has no expiration date' do let(:license) { create(:license, starts_at: Date.current.ago(1.month), expires_at: nil) } before do create(:historical_data, recorded_at: license.starts_at.in(2.days), active_user_count: 10) create(:historical_data, recorded_at: Date.tomorrow, active_user_count: 15) end it 'returns max value for active_user_count until today' do expect(historical_max).to eq(10) end end end describe '#maximum_user_count' do let(:now) { Date.current } it 'returns zero when there is no data' do expect(license.maximum_user_count).to eq(0) end it 'returns historical data' do create(:historical_data, active_user_count: 1) expect(license.maximum_user_count).to eq(1) end it 'returns the billable users count' do create(:usage_trends_measurement, identifier: :billable_users, count: 2) expect(license.maximum_user_count).to eq(2) end it 'returns the daily billable users count when it is higher than historical data' do create(:historical_data, active_user_count: 50) create(:usage_trends_measurement, identifier: :billable_users, count: 100) expect(license.maximum_user_count).to eq(100) end it 'returns historical data when it is higher than the billable users count' do create(:historical_data, active_user_count: 100) create(:usage_trends_measurement, identifier: :billable_users, count: 50) expect(license.maximum_user_count).to eq(100) end it 'returns the correct value when historical data and billable users are equal' do create(:historical_data, active_user_count: 100) create(:usage_trends_measurement, identifier: :billable_users, count: 100) expect(license.maximum_user_count).to eq(100) end it 'returns the highest value from historical data' do create(:historical_data, recorded_at: license.expires_at - 4.months, active_user_count: 130) create(:historical_data, recorded_at: license.expires_at - 3.months, active_user_count: 250) create(:historical_data, recorded_at: license.expires_at - 1.month, active_user_count: 215) expect(license.maximum_user_count).to eq(250) end it 'uses only the most recent billable users entry' do create(:usage_trends_measurement, recorded_at: license.expires_at - 3.months, identifier: :billable_users, count: 150) create(:historical_data, recorded_at: license.expires_at - 3.months, active_user_count: 140) create(:usage_trends_measurement, recorded_at: license.expires_at - 2.months, identifier: :billable_users, count: 100) expect(license.maximum_user_count).to eq(140) end it 'returns the highest historical data since the license started for a 1 year license' do license = build(:license, starts_at: now - 4.months, expires_at: now + 8.months) create(:historical_data, recorded_at: license.starts_at - 1.day, active_user_count: 100) create(:historical_data, recorded_at: now, active_user_count: 40) expect(license.maximum_user_count).to eq(40) end it 'returns the highest historical data since the license started for a license that lasts 6 months' do license = build(:license, starts_at: now - 4.months, expires_at: now + 2.months) create(:historical_data, recorded_at: license.starts_at - 1.day, active_user_count: 80) create(:historical_data, recorded_at: now, active_user_count: 30) expect(license.maximum_user_count).to eq(30) end it 'returns the highest historical data since the license started for a license that lasts two years' do license = build(:license, starts_at: now - 6.months, expires_at: now + 18.months) create(:historical_data, recorded_at: license.starts_at - 1.day, active_user_count: 400) create(:historical_data, recorded_at: now, active_user_count: 300) expect(license.maximum_user_count).to eq(300) end it 'returns the highest historical data during the license period for an expired license' do license = build(:license, starts_at: now - 14.months, expires_at: now - 2.months) create(:historical_data, recorded_at: license.expires_at - 1.month, active_user_count: 400) create(:historical_data, recorded_at: now, active_user_count: 500) expect(license.maximum_user_count).to eq(400) end end describe '#ultimate?' do using RSpec::Parameterized::TableSyntax let(:license) { build(:license, plan: plan) } subject { license.ultimate? } where(:plan, :expected) do nil | false described_class::STARTER_PLAN | false described_class::PREMIUM_PLAN | false described_class::ULTIMATE_PLAN | true end with_them do it { is_expected.to eq(expected) } end end describe 'Trial Licenses' do before do ApplicationSetting.create_from_defaults stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end describe 'Update trial setting' do context 'when the license is not trial' do before do gl_license.restrictions = { trial: false } gl_license.expires_at = Date.tomorrow end it 'does nothing' do license.save! expect(ApplicationSetting.current.license_trial_ends_on).to be_nil end end context 'when the license is the very first trial' do let(:tomorrow) { Date.tomorrow } before do gl_license.restrictions = { trial: true } gl_license.expires_at = tomorrow end it 'is eligible for trial' do expect(described_class.eligible_for_trial?).to be_truthy end it 'updates the trial setting during create' do license.save! expect(described_class.eligible_for_trial?).to be_falsey expect(ApplicationSetting.current.license_trial_ends_on).to eq(tomorrow) end end context 'when the license is a repeated trial' do let(:yesterday) { Date.yesterday } before do gl_license.restrictions = { trial: true } gl_license.expires_at = Date.tomorrow ApplicationSetting.current.update! license_trial_ends_on: yesterday end it 'does not update existing trial setting' do license.save! expect(ApplicationSetting.current.license_trial_ends_on).to eq(yesterday) end it 'is not eligible for trial' do expect(described_class.eligible_for_trial?).to be_falsey end end end end describe '.history' do before_all do described_class.delete_all end it 'does not include the undecryptable license' do undecryptable_license = create(:license) allow(undecryptable_license).to receive(:license).and_return(nil) allow(described_class).to receive(:all).and_return([undecryptable_license]) expect(described_class.history.map(&:id)).to be_empty end it 'returns the licenses sorted by created_at, starts_at and expires_at descending' do today = Date.current now = Time.current past_license = create(:license, created_at: now - 1.month, data: build(:gitlab_license, starts_at: today - 1.month, expires_at: today + 11.months).export) expired_license = create(:license, created_at: now, data: build(:gitlab_license, starts_at: today - 1.year, expires_at: today - 1.month).export) future_license = create(:license, created_at: now, data: build(:gitlab_license, starts_at: today + 1.month, expires_at: today + 13.months).export) another_license = create(:license, created_at: now, data: build(:gitlab_license, starts_at: today - 1.month, expires_at: today + 1.year).export) current_license = create(:license, created_at: now, data: build(:gitlab_license, starts_at: today - 15.days, expires_at: today + 11.months).export) expect(described_class.history.map(&:id)).to eq( [ future_license.id, current_license.id, another_license.id, past_license.id, expired_license.id ] ) end end describe '#edition' do let(:ultimate) { build(:license, plan: 'ultimate') } let(:premium) { build(:license, plan: 'premium') } let(:starter) { build(:license, plan: 'starter') } let(:old) { build(:license, plan: 'other') } it 'have expected values' do expect(ultimate.edition).to eq('EEU') expect(premium.edition).to eq('EEP') expect(starter.edition).to eq('EES') expect(old.edition).to eq('EE') end end def set_restrictions(opts) date = described_class.current.starts_at gl_license.restrictions = { active_user_count: opts[:seats], previous_user_count: opts[:previous_user_count], trueup_period_seat_count: opts[:trueup_period_seat_count], trueup_quantity: opts[:trueup_quantity], trueup_from: (date - 1.year).to_s, trueup_to: date.to_s, reconciliation_completed: opts[:reconciliation_completed] }.compact end describe '#paid?' do where(:plan, :paid_result) do License::STARTER_PLAN | true License::PREMIUM_PLAN | true License::ULTIMATE_PLAN | true nil | true end with_them do let(:license) { build(:license, plan: plan) } subject { license.paid? } it do is_expected.to eq(paid_result) end end end describe '#started?' do where(:starts_at, :result) do (Date.current - 1.month) | true Date.current | true (Date.current + 1.month) | false end with_them do let(:gl_license) { build(:gitlab_license, starts_at: starts_at) } subject { license.started? } it do is_expected.to eq(result) end end end describe '#future_dated?' do where(:starts_at, :result) do (Date.current - 1.month) | false Date.current | false (Date.current + 1.month) | true end with_them do let(:gl_license) { build(:gitlab_license, starts_at: starts_at) } subject { license.future_dated? } it do is_expected.to eq(result) end end end describe '#cloud_license?' do subject { license.cloud_license? } context 'when no license provided' do before do license.data = nil end it { is_expected.to be false } end context 'when the license has cloud licensing disabled' do let(:gl_license) { build(:gitlab_license, :legacy) } it { is_expected.to be false } end context 'when the license has cloud licensing enabled' do let(:gl_license) { build(:gitlab_license, :cloud) } it { is_expected.to be true } end end describe '#offline_cloud_license?' do subject { license.offline_cloud_license? } context 'when no license provided' do before do license.data = nil end it { is_expected.to be false } end context 'when the license is an online cloud license' do let(:gl_license) { build(:gitlab_license, :online) } it { is_expected.to be false } end context 'when the license is an offline cloud license' do let(:gl_license) { build(:gitlab_license, :offline) } it { is_expected.to be true } end context 'when the license only has the cloud attribute set' do let(:gl_license) { build(:gitlab_license, :cloud) } it { is_expected.to be false } end context 'when the license is a legacy license' do let(:gl_license) { build(:gitlab_license, :legacy) } it { is_expected.to be false } end end describe '#online_cloud_license?' do subject { license.online_cloud_license? } context 'when no license provided' do before do license.data = nil end it { is_expected.to be false } end context 'when the license is an offline cloud license' do let(:gl_license) { build(:gitlab_license, :offline) } it { is_expected.to be false } end context 'when the license is an online cloud license' do let(:gl_license) { build(:gitlab_license, :online) } it { is_expected.to be true } end context 'when the license only has the cloud attribute set' do let(:gl_license) { build(:gitlab_license, :cloud) } it { is_expected.to be true } end context 'when the license is a legacy license' do let(:gl_license) { build(:gitlab_license, :legacy) } it { is_expected.to be false } end end describe '#valid_started?' do subject { license.valid_started? } let(:valid) { false } let(:started) { false } before do allow(license).to receive_messages(valid?: valid, started?: started) end context 'when license is invalid' do it { is_expected.to be false } end context 'when license is valid' do let(:valid) { true } context 'when license has not started yet' do it { is_expected.to be false } end context 'when license has started' do let(:started) { true } it { is_expected.to be true } end end end describe '#subscription_cancelled?' do subject { license.subscription_cancelled? } context 'when license is an online cloud license' do let(:gl_license) { build(:gitlab_license, :online) } context 'when license is generated_from_cancellation' do before do gl_license.generated_from_cancellation = true end it { is_expected.to be true } end context 'when license is not generated_from_cancellation' do before do gl_license.generated_from_cancellation = false end it { is_expected.to be false } end end context 'when license is an offline cloud license' do let(:gl_license) { build(:gitlab_license, :offline) } it { is_expected.to be false } end context 'when license is not a cloud license' do let(:gl_license) { build(:gitlab_license) } it { is_expected.to be false } end end describe '#customer_service_enabled?' do subject { license.customer_service_enabled? } context 'when no license provided' do before do license.data = nil end it { is_expected.to be false } end context 'when the license has usage ping required metrics disabled' do let(:gl_license) { build(:gitlab_license, operational_metrics_enabled: false) } it { is_expected.to be false } end context 'when the license has usage ping required metrics enabled' do let(:gl_license) { build(:gitlab_license, operational_metrics_enabled: true) } it { is_expected.to be true } end end describe '#current?' do subject { license.current? } context 'when the license is not persisted' do it { is_expected.to be false } end context 'when the license is persisted' do before do license.save! end context 'when the license is the current license' do it { is_expected.to be true } end context 'when the license is not the current license' do before do allow(described_class).to receive(:current).and_return(create(:license)) end it { is_expected.to be false } end context 'when there is no current license' do before do allow(described_class).to receive(:current).and_return(nil) end it { is_expected.to be false } end end end describe '#license_type' do subject { license.license_type } context 'when the license is not a cloud license' do it { is_expected.to eq(described_class::LEGACY_LICENSE_TYPE) } end context 'when the license is an online cloud license' do let(:gl_license) { build(:gitlab_license, :online) } it { is_expected.to eq(described_class::ONLINE_CLOUD_TYPE) } end context 'when the license is an offline cloud license' do let(:gl_license) { build(:gitlab_license, :offline) } it { is_expected.to eq(described_class::OFFLINE_CLOUD_TYPE) } end end describe '#grace_period_expired?', :freeze_time do subject { license.grace_period_expired? } let(:now) { Time.current } let(:license) { build(:license, starts_at: now - 2.months, expires_at: expires_at) } context 'when license has not expired' do let(:expires_at) { now + 2.months } it { is_expected.to eq(false) } end context 'when license has expired' do let(:expires_at) { now - 10.days } it { is_expected.to eq(false) } end context 'when license has expired more than grace period' do let(:expires_at) { now - described_class::GRACE_PERIOD - 1.day } it { is_expected.to eq(true) } end context 'when license has no expiration date' do let(:expires_at) { nil } it { is_expected.to eq(false) } end end describe '#auto_renew' do it 'is false' do expect(license.auto_renew).to be false end end describe '#active_user_count_threshold' do subject { license.active_user_count_threshold } it 'returns nil for license with unlimited user count' do allow(license).to receive(:seats).and_return(nil) expect(subject).to be_nil end context 'for license with users' do where(:seats, :active_user_count, :percentage, :threshold_value) do 3 | 2 | false | 1 20 | 18 | false | 2 90 | 80 | true | 10 300 | 275 | true | 8 1200 | 1100 | true | 5 end with_them do before do allow(license).to receive(:seats).and_return(seats) allow(license).to receive(:daily_billable_users_count).and_return(active_user_count) end it { is_expected.not_to be_nil } it { is_expected.to include(value: threshold_value, percentage: percentage) } end end end describe '#active_user_count_threshold_reached?' do subject { license.active_user_count_threshold_reached? } where(:seats, :daily_billable_users_count, :result) do 10 | 9 | true nil | 9 | false 10 | 15 | false 100 | 95 | true end with_them do before do allow(license).to receive(:daily_billable_users_count).and_return(daily_billable_users_count) allow(license).to receive(:seats).and_return(seats) end it { is_expected.to eq(result) } end end describe 'checking if there are any seats' do subject { seats.to_i > 0 } where(:seats, :result) do nil | false 0 | false 1 | true 10 | true end with_them do before do allow(license).to receive(:seats).and_return(seats) end it { is_expected.to eq(result) } end end describe '#licensee_name' do subject { license.licensee_name } let(:gl_license) { build(:gitlab_license, licensee: { 'Name' => 'User Example' }) } it { is_expected.to eq('User Example') } end describe '#licensee_email' do subject { license.licensee_email } let(:gl_license) { build(:gitlab_license, licensee: { 'Email' => 'user@example.com' }) } it { is_expected.to eq('user@example.com') } end describe '#licensee_company' do subject { license.licensee_company } let(:gl_license) { build(:gitlab_license, licensee: { 'Company' => 'Example Inc.' }) } it { is_expected.to eq('Example Inc.') } end describe '#activated_at' do subject { license.activated_at } let(:license) do gl_license = build(:gitlab_license, activated_at: activated_at) build(:license, data: gl_license.export, created_at: 5.days.ago) end context 'when activated_at is set within the license data' do let(:activated_at) { Date.yesterday.to_datetime } it { is_expected.to eq(activated_at) } end context 'when activated_at is not set within the license data' do let(:activated_at) { nil } it { is_expected.to eq(license.created_at) } end end describe '#notify_admins?', :freeze_time do subject(:notify_admins?) { license.notify_admins? } context 'when license has expired' do before do gl_license.expires_at = Date.yesterday end it { is_expected.to eq(true) } end context 'when license has no expiration' do before do gl_license.expires_at = nil gl_license.licensee = gl_licensee end it { is_expected.to eq(false) } end context 'when license has not expired' do context 'when license is a trial' do before do gl_license.restrictions = { trial: true } end context 'when license expiration is more than a week from today' do before do gl_license.expires_at = Date.current + 8.days end it { is_expected.to eq(false) } end context 'when license expiration is a week from today' do before do gl_license.expires_at = Date.current + 7.days end it { is_expected.to eq(true) } end context 'when license expiration is less than a week from today' do before do gl_license.expires_at = Date.current + 6.days end it { is_expected.to eq(true) } end end context 'when license is not a trial' do context 'when license expiration is more than 15 days from today' do before do gl_license.expires_at = Date.current + 16.days end it { is_expected.to eq(false) } end context 'when license expiration is 15 days from today' do before do gl_license.expires_at = Date.current + 15.days end it { is_expected.to eq(true) } end context 'when license expiration is less than 15 days from today' do before do gl_license.expires_at = Date.current + 14.days end it { is_expected.to eq(true) } end end end end describe '#notify_users?', :freeze_time do subject(:notify_users?) { license.notify_users? } context 'when license has no expiration' do before do gl_license.expires_at = nil gl_license.licensee = gl_licensee gl_license.block_changes_at = nil end it { is_expected.to eq(false) } end context 'when license is a trial' do before do gl_license.restrictions = { trial: true } end context 'when license expiration is more than a week from today' do before do gl_license.expires_at = Date.current + 8.days end it { is_expected.to eq(false) } end context 'when license expiration is a week from today' do before do gl_license.expires_at = Date.current + 7.days end it { is_expected.to eq(true) } end context 'when license expiration is less than a week from today' do before do gl_license.expires_at = Date.current + 6.days end it { is_expected.to eq(true) } end end context 'when license is not a trial' do context 'when license block changes date is before today' do before do gl_license.block_changes_at = Date.current - 1.day end it { is_expected.to eq(true) } end context 'when license block changes date is today' do before do gl_license.block_changes_at = Date.current end it { is_expected.to eq(true) } end context 'when license block changes date is after today' do before do gl_license.block_changes_at = Date.current + 1.day end it { is_expected.to eq(false) } end end end end