From eae9e04a5ff198ae59e00ed8f4aa28328c992070 Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Mon, 17 Mar 2025 15:44:10 +0100 Subject: [PATCH 1/6] Update billable user counts on license creation --- ee/app/models/license.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index ebb3af11940086..06b3c1f5fee773 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -47,6 +47,7 @@ class License < ApplicationRecord after_create :update_trial_setting after_commit :reset_current after_commit :reset_future_dated, on: [:create, :destroy] + after_commit :update_billable_user_counts, on: :create scope :cloud, -> { where(cloud: true) } scope :recent, -> { reorder(id: :desc) } @@ -361,6 +362,13 @@ def update_trial_setting settings.update license_trial_ends_on: license.expires_at end + def update_billable_user_counts + return unless started? + + identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users] + ::Analytics::UsageTrends::CounterJobWorker.perform_async(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now) + end + def paid? [License::STARTER_PLAN, License::PREMIUM_PLAN, License::ULTIMATE_PLAN].include?(plan) end -- GitLab From 9a6a3c971d93a85fe8afd0b97844d985fd040e87 Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Mon, 17 Mar 2025 17:06:59 +0100 Subject: [PATCH 2/6] Add test for after_commit method --- ee/spec/models/license_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ee/spec/models/license_spec.rb b/ee/spec/models/license_spec.rb index 09807dd1476cb9..4e45fcf9a961f8 100644 --- a/ee/spec/models/license_spec.rb +++ b/ee/spec/models/license_spec.rb @@ -626,6 +626,22 @@ def current_license_cached_value end end end + + describe '#update_billable_user_counts' do + subject(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } + + let(:now) { Time.zone.now } + + around do |example| + travel_to(now) { example.run } + end + + it 'updates billable user counts' do + subject + + expect(Analytics::UsageTrends::CounterJobWorker.jobs.count).to eq(1) + end + end end describe 'Scopes' do -- GitLab From d033e8e7972439cc9100b8c51fb041cf23d5454c Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Mon, 24 Mar 2025 12:22:17 +0100 Subject: [PATCH 3/6] Correct started? check + update specs --- ee/app/models/license.rb | 2 +- ee/spec/models/license_spec.rb | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index 06b3c1f5fee773..39bf64b014cba6 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -363,7 +363,7 @@ def update_trial_setting end def update_billable_user_counts - return unless started? + return unless starts_at && started? identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users] ::Analytics::UsageTrends::CounterJobWorker.perform_async(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now) diff --git a/ee/spec/models/license_spec.rb b/ee/spec/models/license_spec.rb index 4e45fcf9a961f8..1e6870aeb5af37 100644 --- a/ee/spec/models/license_spec.rb +++ b/ee/spec/models/license_spec.rb @@ -18,6 +18,10 @@ def build_license_with_add_ons(add_ons, plan: nil) build(:license, data: gl_license.export) end + before do + allow(::Analytics::UsageTrends::CounterJobWorker).to receive(:perform_async) + end + describe 'validations' do describe '#valid_license' do subject(:license) { build(:license, data: gl_license.class.encryptor.encrypt(gl_license.to_json)) } @@ -630,16 +634,10 @@ def current_license_cached_value describe '#update_billable_user_counts' do subject(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } - let(:now) { Time.zone.now } - - around do |example| - travel_to(now) { example.run } - end - it 'updates billable user counts' do subject - expect(Analytics::UsageTrends::CounterJobWorker.jobs.count).to eq(1) + expect(::Analytics::UsageTrends::CounterJobWorker).to have_received(:perform_async).once end end end -- GitLab From 88c63302c6484a53995e26b05a473249a9950422 Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Mon, 24 Mar 2025 15:42:03 +0100 Subject: [PATCH 4/6] Add more test cases --- ee/spec/models/license_spec.rb | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ee/spec/models/license_spec.rb b/ee/spec/models/license_spec.rb index 1e6870aeb5af37..0ceaf526629b90 100644 --- a/ee/spec/models/license_spec.rb +++ b/ee/spec/models/license_spec.rb @@ -632,12 +632,28 @@ def current_license_cached_value end describe '#update_billable_user_counts' do - subject(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } + context 'when license has started' do + subject!(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } - it 'updates billable user counts' do - subject + it 'updates billable user counts' do + expect(::Analytics::UsageTrends::CounterJobWorker).to have_received(:perform_async).once + end + end - expect(::Analytics::UsageTrends::CounterJobWorker).to have_received(:perform_async).once + context 'when license has not started' do + subject!(:license) { create(:license, data: gl_license.export, starts_at: Date.tomorrow) } + + it 'does not update billable user counts' do + expect(::Analytics::UsageTrends::CounterJobWorker).not_to have_received(:perform_async) + end + end + + context 'when license has no start date' do + subject!(:license) { create(:license, data: gl_license.export, starts_at: nil) } + + it 'does not update billable user counts' do + expect(::Analytics::UsageTrends::CounterJobWorker).not_to have_received(:perform_async) + end end end end -- GitLab From b64d86cec203e01cc92cde1fdedaaa460ba102cd Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Tue, 1 Apr 2025 14:00:03 +0200 Subject: [PATCH 5/6] Update billable user count for any start date --- ee/app/models/license.rb | 2 -- ee/spec/models/license_spec.rb | 24 +++--------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/ee/app/models/license.rb b/ee/app/models/license.rb index 39bf64b014cba6..bcba572ab94bc3 100644 --- a/ee/app/models/license.rb +++ b/ee/app/models/license.rb @@ -363,8 +363,6 @@ def update_trial_setting end def update_billable_user_counts - return unless starts_at && started? - identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users] ::Analytics::UsageTrends::CounterJobWorker.perform_async(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now) end diff --git a/ee/spec/models/license_spec.rb b/ee/spec/models/license_spec.rb index 0ceaf526629b90..968f8eb515c42d 100644 --- a/ee/spec/models/license_spec.rb +++ b/ee/spec/models/license_spec.rb @@ -632,28 +632,10 @@ def current_license_cached_value end describe '#update_billable_user_counts' do - context 'when license has started' do - subject!(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } - - it 'updates billable user counts' do - expect(::Analytics::UsageTrends::CounterJobWorker).to have_received(:perform_async).once - end - end - - context 'when license has not started' do - subject!(:license) { create(:license, data: gl_license.export, starts_at: Date.tomorrow) } + subject!(:license) { create(:license, data: gl_license.export, starts_at: Date.current) } - it 'does not update billable user counts' do - expect(::Analytics::UsageTrends::CounterJobWorker).not_to have_received(:perform_async) - end - end - - context 'when license has no start date' do - subject!(:license) { create(:license, data: gl_license.export, starts_at: nil) } - - it 'does not update billable user counts' do - expect(::Analytics::UsageTrends::CounterJobWorker).not_to have_received(:perform_async) - end + it 'updates billable user counts' do + expect(::Analytics::UsageTrends::CounterJobWorker).to have_received(:perform_async).once end end end -- GitLab From 0cbce99bff6326f0fda5bc8f7986c8393544bbe1 Mon Sep 17 00:00:00 2001 From: Mohamed Moustafa Date: Thu, 3 Apr 2025 14:43:16 +0200 Subject: [PATCH 6/6] Correct API::License refresh_billable_users spec * Don't lazy load License object creation, so we don't accidentally catch the first CounterJobWorker trigger from the License after_create event. --- ee/spec/requests/api/license_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/spec/requests/api/license_spec.rb b/ee/spec/requests/api/license_spec.rb index f8c12a0004c8c9..3dcec170b170c5 100644 --- a/ee/spec/requests/api/license_spec.rb +++ b/ee/spec/requests/api/license_spec.rb @@ -235,7 +235,7 @@ def license_json(license) end describe 'PUT /license/:id/refresh_billable_users' do - let(:license) { create(:license) } + let!(:license) { create(:license) } let(:endpoint) { "/license/#{license.id}/refresh_billable_users" } before do -- GitLab