From c4820da588d0d76b4b6378f821211a00f20048e3 Mon Sep 17 00:00:00 2001
From: Mark Chao <mchao@gitlab.com>
Date: Mon, 7 Sep 2020 15:01:25 +0800
Subject: [PATCH] Extract attribute refresh logic

Add spec
---
 ee/app/models/gitlab_subscription.rb          |  7 ++
 ...sed_for_gitlab_com_subscriptions_worker.rb |  7 +-
 ee/spec/models/gitlab_subscription_spec.rb    | 32 ++++++
 ...or_gitlab_com_subscriptions_worker_spec.rb | 97 +++++++++++--------
 4 files changed, 95 insertions(+), 48 deletions(-)

diff --git a/ee/app/models/gitlab_subscription.rb b/ee/app/models/gitlab_subscription.rb
index e58699fa9185..03e6cd439052 100644
--- a/ee/app/models/gitlab_subscription.rb
+++ b/ee/app/models/gitlab_subscription.rb
@@ -56,6 +56,13 @@ def calculate_seats_owed
     [0, max_seats_used - seats].max
   end
 
+  # Refresh seat related attribute (without persisting them)
+  def refresh_seat_attributes!
+    self.seats_in_use = calculate_seats_in_use
+    self.max_seats_used = [max_seats_used, seats_in_use].max
+    self.seats_owed = calculate_seats_owed
+  end
+
   def has_a_paid_hosted_plan?(include_trials: false)
     (include_trials || !trial?) &&
       hosted? &&
diff --git a/ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb b/ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb
index e7f341885fcf..2079b0ef595f 100644
--- a/ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb
+++ b/ee/app/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker.rb
@@ -16,12 +16,9 @@ def perform
       tuples = []
 
       subscriptions.each do |subscription|
-        seats_in_use = subscription.calculate_seats_in_use
-        max_seats_used = [subscription.max_seats_used, seats_in_use].max
-        subscription.max_seats_used = max_seats_used
-        seats_owed = subscription.calculate_seats_owed
+        subscription.refresh_seat_attributes!
 
-        tuples << [subscription.id, max_seats_used, seats_in_use, seats_owed]
+        tuples << [subscription.id, subscription.max_seats_used, subscription.seats_in_use, subscription.seats_owed]
       end
 
       if tuples.present?
diff --git a/ee/spec/models/gitlab_subscription_spec.rb b/ee/spec/models/gitlab_subscription_spec.rb
index 87258baf947f..4db38cda207d 100644
--- a/ee/spec/models/gitlab_subscription_spec.rb
+++ b/ee/spec/models/gitlab_subscription_spec.rb
@@ -176,6 +176,38 @@
     end
   end
 
+  describe '#refresh_seat_attributes!' do
+    subject { create(:gitlab_subscription, seats: 3, max_seats_used: 2) }
+
+    before do
+      expect(subject).to receive(:calculate_seats_in_use).and_return(calculate_seats_in_use)
+    end
+
+    context 'when current seats in use is lower than recorded max_seats_used' do
+      let(:calculate_seats_in_use) { 1 }
+
+      it 'does not increase max_seats_used' do
+        expect do
+          subject.refresh_seat_attributes!
+        end.to change(subject, :seats_in_use).from(0).to(1)
+          .and not_change(subject, :max_seats_used)
+          .and not_change(subject, :seats_owed)
+      end
+    end
+
+    context 'when current seats in use is higher than seats and max_seats_used' do
+      let(:calculate_seats_in_use) { 4 }
+
+      it 'increases seats and max_seats_used' do
+        expect do
+          subject.refresh_seat_attributes!
+        end.to change(subject, :seats_in_use).from(0).to(4)
+          .and change(subject, :max_seats_used).from(2).to(4)
+          .and change(subject, :seats_owed).from(0).to(1)
+      end
+    end
+  end
+
   describe '#expired?' do
     let(:gitlab_subscription) { create(:gitlab_subscription, end_date: end_date) }
 
diff --git a/ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb b/ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb
index 62b38771f1a1..a518335c8289 100644
--- a/ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb
+++ b/ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb
@@ -5,11 +5,10 @@
 RSpec.describe UpdateMaxSeatsUsedForGitlabComSubscriptionsWorker do
   subject { described_class.new }
 
-  let_it_be(:user)                { create(:user) }
-  let_it_be(:group)               { create(:group) }
-  let_it_be(:bronze_plan)         { create(:bronze_plan) }
-  let_it_be(:early_adopter_plan)  { create(:early_adopter_plan) }
-  let_it_be(:gitlab_subscription, refind: true) { create(:gitlab_subscription, namespace: group, seats: 1) }
+  let_it_be(:bronze_plan) { create(:bronze_plan) }
+  let_it_be(:early_adopter_plan) { create(:early_adopter_plan) }
+  let_it_be(:gitlab_subscription, refind: true) { create(:gitlab_subscription, seats: 1) }
+  let_it_be(:gitlab_subscription_2, refind: true) { create(:gitlab_subscription, seats: 11) }
 
   let(:db_is_read_only) { false }
   let(:subscription_attrs) { nil }
@@ -18,22 +17,54 @@
     allow(Gitlab::Database).to receive(:read_only?) { db_is_read_only }
     allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { true }
 
-    group.add_developer(user)
+    allow_next_found_instance_of(GitlabSubscription) do |subscription|
+      allow(subscription).to receive(:refresh_seat_attributes!) do
+        subscription.max_seats_used = subscription.seats + 3
+        subscription.seats_in_use = subscription.seats + 2
+        subscription.seats_owed = subscription.seats + 1
+      end
+    end
+  end
+
+  def perform_and_reload
+    subject.perform
+    gitlab_subscription.reload
+    gitlab_subscription_2.reload
+  end
+
+  shared_examples 'updates nothing' do
+    it 'does not update seat columns' do
+      expect do
+        perform_and_reload
+      end.to not_change(gitlab_subscription, :max_seats_used)
+        .and not_change(gitlab_subscription, :seats_in_use)
+        .and not_change(gitlab_subscription, :seats_owed)
+        .and not_change(gitlab_subscription_2, :max_seats_used)
+        .and not_change(gitlab_subscription_2, :seats_in_use)
+        .and not_change(gitlab_subscription_2, :seats_owed)
+    end
   end
 
-  shared_examples 'keeps original max_seats_used value' do
-    it 'does not update max_seats_used' do
-      expect { subject.perform }.not_to change { gitlab_subscription.reload.max_seats_used }
+  shared_examples 'updates only paid plans' do
+    it "persists seat attributes after refresh_seat_attributes! for only paid plans" do
+      expect do
+        perform_and_reload
+      end.to not_change(gitlab_subscription, :max_seats_used)
+        .and not_change(gitlab_subscription, :seats_in_use)
+        .and not_change(gitlab_subscription, :seats_owed)
+        .and change(gitlab_subscription_2, :max_seats_used).from(0).to(14)
+        .and change(gitlab_subscription_2, :seats_in_use).from(0).to(13)
+        .and change(gitlab_subscription_2, :seats_owed).from(0).to(12)
     end
   end
 
   context 'where the DB is read only' do
     let(:db_is_read_only) { true }
 
-    include_examples 'keeps original max_seats_used value'
+    include_examples 'updates nothing'
   end
 
-  context 'when the DB PostgreSQK AND is not read only' do
+  context 'when the DB is not read only' do
     before do
       gitlab_subscription.update!(subscription_attrs) if subscription_attrs
     end
@@ -41,56 +72,36 @@
     context 'with a free plan' do
       let(:subscription_attrs) { { hosted_plan: nil } }
 
-      include_examples 'keeps original max_seats_used value'
+      include_examples 'updates only paid plans'
     end
 
     context 'with a trial plan' do
       let(:subscription_attrs) { { hosted_plan: bronze_plan, trial: true } }
 
-      include_examples 'keeps original max_seats_used value'
+      include_examples 'updates only paid plans'
     end
 
     context 'with an early adopter plan' do
       let(:subscription_attrs) { { hosted_plan: early_adopter_plan } }
 
-      include_examples 'keeps original max_seats_used value'
+      include_examples 'updates only paid plans'
     end
 
     context 'with a paid plan', :aggregate_failures do
-      let_it_be(:other_user) { create(:user) }
-      let_it_be(:other_group) { create(:group) }
-      let_it_be(:other_gitlab_subscription, refind: true) { create(:gitlab_subscription, namespace: other_group, seats: 1) }
-
       before do
-        group.add_developer(other_user)
-        other_group.add_developer(other_user)
-
         gitlab_subscription.update!(hosted_plan: bronze_plan)
-        other_gitlab_subscription.update!(hosted_plan: bronze_plan)
-      end
-
-      it 'only updates max_seats_used if active users count is greater than it' do
-        expect do
-          subject.perform
-          gitlab_subscription.reload
-          other_gitlab_subscription.reload
-        end.to change(gitlab_subscription, :max_seats_used).from(0).to(2)
-          .and change(gitlab_subscription, :seats_in_use).from(0).to(2)
-          .and change(gitlab_subscription, :seats_owed).from(0).to(1)
-          .and change(other_gitlab_subscription, :max_seats_used).from(0).to(1)
-          .and change(other_gitlab_subscription, :seats_in_use).from(0).to(1)
-          .and not_change(other_gitlab_subscription, :seats_owed).from(0)
+        gitlab_subscription_2.update!(hosted_plan: bronze_plan)
       end
 
-      it 'does not update max_seats_used if active users count is lower than it' do
-        gitlab_subscription.update_column(:max_seats_used, 5)
-
+      it 'persists seat attributes after refresh_seat_attributes' do
         expect do
-          subject.perform
-          gitlab_subscription.reload
-        end.to change(gitlab_subscription, :seats_in_use).from(0).to(2)
-          .and change(gitlab_subscription, :seats_owed).from(0).to(4)
-          .and not_change(gitlab_subscription, :max_seats_used).from(5)
+          perform_and_reload
+        end.to change(gitlab_subscription, :max_seats_used).from(0).to(4)
+          .and change(gitlab_subscription, :seats_in_use).from(0).to(3)
+          .and change(gitlab_subscription, :seats_owed).from(0).to(2)
+          .and change(gitlab_subscription_2, :max_seats_used).from(0).to(14)
+          .and change(gitlab_subscription_2, :seats_in_use).from(0).to(13)
+          .and change(gitlab_subscription_2, :seats_owed).from(0).to(12)
       end
     end
   end
-- 
GitLab